/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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
 *
 *      https://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 org.apache.tools.ant.taskdefs.modules;

import java.io.File;
import java.io.PrintStream;
import java.io.ByteArrayOutputStream;
import java.io.Reader;
import java.io.IOException;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import java.nio.file.Files;
import java.nio.file.FileVisitResult;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

import java.util.Collection;
import java.util.List;
import java.util.ArrayList;

import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Properties;

import java.util.Collections;
import java.util.Objects;

import java.util.spi.ToolProvider;

import java.util.stream.Stream;
import java.util.stream.Collectors;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;

import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.LogLevel;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.ResourceCollection;

import org.apache.tools.ant.util.CompositeMapper;
import org.apache.tools.ant.util.MergingMapper;

import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.ResourceUtils;

Assembles jmod files into an executable image. Equivalent to the JDK jlink command.

Supported attributes:

destDir
Root directory of created image. (required)
modulePath
Path of modules. Should be a list of .jmod files. Required, unless nested module path or modulepathref is present.
modulePathRef
Reference to path of modules. Referenced path should be a list of .jmod files.
modules
Comma-separated list of modules to assemble. Required, unless one or more nested <module> elements are present.
observableModules
Comma-separated list of explicit modules that comprise "universe" visible to tool while linking.
launchers
Comma-separated list of commands, each of the form name=module or name=module/mainclass
excludeFiles
Comma-separated list of patterns specifying files to exclude from linked image. Each is either a standard PathMatcher pattern or @filename.
excludeResources
Comma-separated list of patterns specifying resources to exclude from jmods. Each is either a standard PathMatcher pattern or @filename.
locales
Comma-separated list of extra locales to include, requires jdk.localedata module
resourceOrder
Comma-separated list of patterns specifying resource search order. Each is either a standard PathMatcher pattern or @filename.
bindServices
boolean, whether to link service providers; default is false
ignoreSigning
boolean, whether to allow signed jar files; default is false
includeHeaders
boolean, whether to include header files; default is true
includeManPages
boolean, whether to include man pages; default is true
includeNativeCommands
boolean, whether to include native executables normally generated for image; default is true
debug
boolean, whether to include debug information; default is true
verboseLevel
If set, jlink will produce verbose output, which will be logged at the specified Ant log level (DEBUG, VERBOSE, INFO}, WARN, or ERR).
compress
compression level, one of:
0
none
no compression (default)
1
strings
constant string sharing
2
zip
zip compression
endianness
Must be little or big, default is native endianness
checkDuplicateLegal
Boolean. When merging legal notices from different modules because they have the same name, verify that their contents are identical. Default is false, which means any license files with the same name are assumed to have the same content, and no checking is done.
vmType
Hotspot VM in image, one of:
  • client
  • server
  • minimal
  • all (default)

Supported nested elements

<modulepath>
path element
<module>
May be specified multiple times. Only attribute is required name attribute.
<observableModule>
May be specified multiple times. Only attribute is required name attribute.
<launcher>
May be specified multiple times. Attributes:
  • name (required)
  • module (required)
  • mainClass (optional)
<locale>
May be specified multiple times. Only attribute is required name attribute.
<resourceOrder>
Explicit resource search order in image. May be specified multiple times. Exactly one of these attributes must be specified:
pattern
A standard PathMatcher pattern
listFile
Text file containing list of resource names (not patterns), one per line
If the resourceOrder attribute is also present on the task, its patterns are treated as if they occur before patterns in nested <resourceOrder> elements.
<excludeFiles>
Excludes files from linked image tree. May be specified multiple times. Exactly one of these attributes is required:
pattern
A standard PathMatcher pattern
listFile
Text file containing list of file names (not patterns), one per line
<excludeResources>
Excludes resources from jmods. May be specified multiple times. Exactly one of these attributes is required:
pattern
A standard PathMatcher pattern
listFile
Text file containing list of resource names (not patterns), one per line
<compress>
Must have level attribute, whose permitted values are the same as the compress task attribute described above. May also have a files attribute, which is a comma-separated list of patterns, and/or nested <files> elements, each with either a pattern attribute or listFile attribute.
<releaseInfo>
Replaces, augments, or trims the image's release info properties. This may specify any of the following:
  • A file attribute, pointing to a Java properties file containing new release info properties that will entirely replace the current ones.
  • A delete attribute, containing comma-separated property keys to remove from application's release info, and/or any number of nested <delete> elements, each with a required key attribute.
  • One or more nested <add> elements, containing either key and value attributes, or a file attribute and an optional charset attribute.
See Also:
Since:1.10.6
/** * Assembles jmod files into an executable image. Equivalent to the * JDK {@code jlink} command. * <p> * Supported attributes: * <dl> * <dt>{@code destDir} * <dd>Root directory of created image. (required) * <dt>{@code modulePath} * <dd>Path of modules. Should be a list of .jmod files. Required, unless * nested module path or modulepathref is present. * <dt>{@code modulePathRef} * <dd>Reference to path of modules. Referenced path should be * a list of .jmod files. * <dt>{@code modules} * <dd>Comma-separated list of modules to assemble. Required, unless * one or more nested {@code <module>} elements are present. * <dt>{@code observableModules} * <dd>Comma-separated list of explicit modules that comprise * "universe" visible to tool while linking. * <dt>{@code launchers} * <dd>Comma-separated list of commands, each of the form * <var>name</var>{@code =}<var>module</var> or * <var>name</var>{@code =}<var>module</var>{@code /}<var>mainclass</var> * <dt>{@code excludeFiles} * <dd>Comma-separated list of patterns specifying files to exclude from * linked image. * Each is either a <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher%28java.lang.String%29">standard PathMatcher pattern</a> * or {@code @}<var>filename</var>. * <dt>{@code excludeResources} * <dd>Comma-separated list of patterns specifying resources to exclude from jmods. * Each is either a <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher%28java.lang.String%29">standard PathMatcher pattern</a> * or {@code @}<var>filename</var>. * <dt>{@code locales} * <dd>Comma-separated list of extra locales to include, * requires {@code jdk.localedata} module * <dt>{@code resourceOrder} * <dt>Comma-separated list of patterns specifying resource search order. * Each is either a <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher%28java.lang.String%29">standard PathMatcher pattern</a> * or {@code @}<var>filename</var>. * <dt>{@code bindServices} * <dd>boolean, whether to link service providers; default is false * <dt>{@code ignoreSigning} * <dd>boolean, whether to allow signed jar files; default is false * <dt>{@code includeHeaders} * <dd>boolean, whether to include header files; default is true * <dt>{@code includeManPages} * <dd>boolean, whether to include man pages; default is true * <dt>{@code includeNativeCommands} * <dd>boolean, whether to include native executables normally generated * for image; default is true * <dt>{@code debug} * <dd>boolean, whether to include debug information; default is true * <dt>{@code verboseLevel} * <dd>If set, jlink will produce verbose output, which will be logged at * the specified Ant log level ({@code DEBUG}, {@code VERBOSE}, * {@code INFO}}, {@code WARN}, or {@code ERR}). * <dt>{@code compress} * <dd>compression level, one of: * <dl> * <dt>{@code 0} * <dt>{@code none} * <dd>no compression (default) * <dt>{@code 1} * <dt>{@code strings} * <dd>constant string sharing * <dt>{@code 2} * <dt>{@code zip} * <dd>zip compression * </dl> * <dt>{@code endianness} * <dd>Must be {@code little} or {@code big}, default is native endianness * <dt>{@code checkDuplicateLegal} * <dd>Boolean. When merging legal notices from different modules * because they have the same name, verify that their contents * are identical. Default is false, which means any license files * with the same name are assumed to have the same content, and no * checking is done. * <dt>{@code vmType} * <dd>Hotspot VM in image, one of: * <ul> * <li>{@code client} * <li>{@code server} * <li>{@code minimal} * <li>{@code all} (default) * </ul> * </dl> * * <p> * Supported nested elements * <dl> * <dt>{@code <modulepath>} * <dd>path element * <dt>{@code <module>} * <dd>May be specified multiple times. * Only attribute is required {@code name} attribute. * <dt>{@code <observableModule>} * <dd>May be specified multiple times. * Only attribute is required {@code name} attribute. * <dt>{@code <launcher>} * <dd>May be specified multiple times. Attributes: * <ul> * <li>{@code name} (required) * <li>{@code module} (required) * <li>{@code mainClass} (optional) * </ul> * <dt>{@code <locale>} * <dd>May be specified multiple times. * Only attribute is required {@code name} attribute. * <dt>{@code <resourceOrder>} * <dd>Explicit resource search order in image. May be specified multiple * times. Exactly one of these attributes must be specified: * <dl> * <dt>{@code pattern} * <dd>A <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher%28java.lang.String%29">standard PathMatcher pattern</a> * <dt>{@code listFile} * <dd>Text file containing list of resource names (not patterns), * one per line * </dl> * If the {@code resourceOrder} attribute is also present on the task, its * patterns are treated as if they occur before patterns in nested * {@code <resourceOrder>} elements. * <dt>{@code <excludeFiles>} * <dd>Excludes files from linked image tree. May be specified multiple times. * Exactly one of these attributes is required: * <dl> * <dt>{@code pattern} * <dd>A <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher%28java.lang.String%29">standard PathMatcher pattern</a> * <dt>{@code listFile} * <dd>Text file containing list of file names (not patterns), * one per line * </dl> * <dt>{@code <excludeResources>} * <dd>Excludes resources from jmods. May be specified multiple times. * Exactly one of these attributes is required: * <dl> * <dt>{@code pattern} * <dd>A <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher%28java.lang.String%29">standard PathMatcher pattern</a> * <dt>{@code listFile} * <dd>Text file containing list of resource names (not patterns), * one per line * </dl> * <dt>{@code <compress>} * <dd>Must have {@code level} attribute, whose permitted values are the same * as the {@code compress} task attribute described above. * May also have a {@code files} attribute, which is a comma-separated * list of patterns, and/or nested {@code <files>} elements, each with * either a {@code pattern} attribute or {@code listFile} attribute. * <dt>{@code <releaseInfo>} * <dd>Replaces, augments, or trims the image's release info properties. * This may specify any of the following: * <ul> * <li>A {@code file} attribute, pointing to a Java properties file * containing new release info properties that will entirely replace * the current ones. * <li>A {@code delete} attribute, containing comma-separated property keys * to remove from application's release info, and/or any number of * nested {@code <delete>} elements, each with a required {@code key} * attribute. * <li>One or more nested {@code <add>} elements, containing either * {@code key} and {@code value} attributes, or a {@code file} * attribute and an optional {@code charset} attribute. * </ul> * </dl> * * @see <a href="https://docs.oracle.com/en/java/javase/11/tools/jlink.html"><code>jlink</code> tool reference</a> * * @since 1.10.6 */
public class Link extends Task {
Error message for improperly formatted launcher attribute.
/** * Error message for improperly formatted launcher attribute. */
private static final String INVALID_LAUNCHER_STRING = "Launcher command must take the form name=module " + "or name=module/mainclass";
Path of directories containing linkable modules.
/** Path of directories containing linkable modules. */
private Path modulePath;
Modules to include in linked image.
/** Modules to include in linked image. */
private final List<ModuleSpec> modules = new ArrayList<>();
If non-empty, list of all modules linker is permitted to know about.
/** If non-empty, list of all modules linker is permitted to know about. */
private final List<ModuleSpec> observableModules = new ArrayList<>();
Additional runnable programs which linker will place in image's bin directory.
/** * Additional runnable programs which linker will place in image's * <code>bin</code> directory. */
private final List<Launcher> launchers = new ArrayList<>();
Locales to explicitly include from jdk.localdata module. If empty, all locales are included.
/** * Locales to explicitly include from {@code jdk.localdata} module. * If empty, all locales are included. */
private final List<LocaleSpec> locales = new ArrayList<>();
Resource ordering.
/** Resource ordering. */
private final List<PatternListEntry> ordering = new ArrayList<>();
Files to exclude from linked image.
/** Files to exclude from linked image. */
private final List<PatternListEntry> excludedFiles = new ArrayList<>();
Resources in linked modules which should be excluded from linked image.
/** * Resources in linked modules which should be excluded from linked image. */
private final List<PatternListEntry> excludedResources = new ArrayList<>();
Whether to include all service provides in linked image which are present in the module path and which are needed by modules explicitly linked.
/** * Whether to include all service provides in linked image which are * present in the module path and which are needed by modules explicitly * linked. */
private boolean bindServices;
Whether to ignore signed jars (and jmods based on signed jars) when linking, instead of emitting an error.
/** * Whether to ignore signed jars (and jmods based on signed jars) when * linking, instead of emitting an error. */
private boolean ignoreSigning;
Whether to include header files from linked modules in image.
/** Whether to include header files from linked modules in image. */
private boolean includeHeaders = true;
Whether to include man pages from linked modules in image.
/** Whether to include man pages from linked modules in image. */
private boolean includeManPages = true;
Whether to include native commands from linked modules in image.
/** Whether to include native commands from linked modules in image. */
private boolean includeNativeCommands = true;
Whether to include classes' debug information or strip it.
/** Whether to include classes' debug information or strip it. */
private boolean debug = true;
The Ant logging level at which verbose output of linked should be emitted. If null, verbose output is disabled.
/** * The Ant logging level at which verbose output of linked should be * emitted. If null, verbose output is disabled. */
private LogLevel verboseLevel;
Directory into which linked image will be placed.
/** Directory into which linked image will be placed. */
private File outputDir;
Endianness of some files (?) in linked image.
/** Endianness of some files (?) in linked image. */
private Endianness endianness;
Simple compression level applied to linked image. This or compression may be set, but not both.
/** * Simple compression level applied to linked image. * This or {@link #compression} may be set, but not both. */
private CompressionLevel compressionLevel;
Describes which files in image to compress, and how to compress them. This or compressionLevel may be set, but not both.
/** * Describes which files in image to compress, and how to compress them. * This or {@link #compressionLevel} may be set, but not both. */
private Compression compression;
Whether to check duplicate legal notices from different modules actually have identical content, not just indentical names, before merging them. Forced to true as of Java 11.
/** * Whether to check duplicate legal notices from different modules * actually have identical content, not just indentical names, * before merging them. * <a href="https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java#L80">Forced to true as of Java 11.</a> */
private boolean checkDuplicateLegal;
Type of VM in linked image.
/** Type of VM in linked image. */
private VMType vmType;
Changes to linked image's default release info.
/** Changes to linked image's default release info. */
private final List<ReleaseInfo> releaseInfo = new ArrayList<>();
Adds child <modulePath> element.
See Also:
Returns:new, empty child element
/** * Adds child {@code <modulePath>} element. * * @return new, empty child element * * @see #setModulePath(Path) */
public Path createModulePath() { if (modulePath == null) { modulePath = new Path(getProject()); } return modulePath.createPath(); }
Attribute containing path of directories containing linkable modules.
See Also:
Returns:current module path, possibly null
/** * Attribute containing path of directories containing linkable modules. * * @return current module path, possibly {@code null} * * @see #setModulePath(Path) * @see #createModulePath() */
public Path getModulePath() { return modulePath; }
Sets attribute containing path of directories containing linkable modules.
Params:
  • path – new module path
See Also:
/** * Sets attribute containing path of directories containing * linkable modules. * * @param path new module path * * @see #getModulePath() * @see #setModulePathRef(Reference) * @see #createModulePath() */
public void setModulePath(final Path path) { if (modulePath == null) { this.modulePath = path; } else { modulePath.append(path); } }
Sets module path as a reference.
Params:
  • ref – path reference
See Also:
/** * Sets module path as a reference. * * @param ref path reference * * @see #setModulePath(Path) * @see #createModulePath() */
public void setModulePathRef(final Reference ref) { createModulePath().setRefid(ref); }
Adds child <module> element, specifying a module to link.
See Also:
Returns:new, unconfigured child element
/** * Adds child {@code <module>} element, specifying a module to link. * * @return new, unconfigured child element * * @see #setModules(String) */
public ModuleSpec createModule() { ModuleSpec module = new ModuleSpec(); modules.add(module); return module; }
Sets attribute containing list of modules to link.
Params:
  • moduleList – comma-separated list of module names
/** * Sets attribute containing list of modules to link. * * @param moduleList comma-separated list of module names */
public void setModules(final String moduleList) { for (String moduleName : moduleList.split(",")) { modules.add(new ModuleSpec(moduleName)); } }
Creates child <observableModule> element that represents one of the modules the linker is permitted to know about.
Returns:new, unconfigured child element
/** * Creates child {@code <observableModule>} element that represents * one of the modules the linker is permitted to know about. * * @return new, unconfigured child element */
public ModuleSpec createObservableModule() { ModuleSpec module = new ModuleSpec(); observableModules.add(module); return module; }
Sets attribute containing modules linker is permitted to know about.
Params:
  • moduleList – comma-separated list of module names
/** * Sets attribute containing modules linker is permitted to know about. * * @param moduleList comma-separated list of module names */
public void setObservableModules(final String moduleList) { for (String moduleName : moduleList.split(",")) { observableModules.add(new ModuleSpec(moduleName)); } }
Creates child <launcher> element that can contain information on additional executable in the linked image.
See Also:
Returns:new, unconfigured child element
/** * Creates child {@code <launcher>} element that can contain information * on additional executable in the linked image. * * @return new, unconfigured child element * * @see #setLaunchers(String) */
public Launcher createLauncher() { Launcher command = new Launcher(); launchers.add(command); return command; }
Sets attribute containing comma-separated list of information needed for additional executables in the linked image. Each item must be of the form * name=module or name=module/mainclass.
Params:
  • launcherList – comma-separated list of launcher data
/** * Sets attribute containing comma-separated list of information needed for * additional executables in the linked image. Each item must be of the * form * <var>name</var>{@code =}<var>module</var> or * <var>name</var>{@code =}<var>module</var>{@code /}<var>mainclass</var>. * * @param launcherList comma-separated list of launcher data */
public void setLaunchers(final String launcherList) { for (String launcherSpec : launcherList.split(",")) { launchers.add(new Launcher(launcherSpec)); } }
Creates child <locale> element that specifies a Java locale, or set of locales, to include from the jdk.localedata module in the linked image.
Returns:new, unconfigured child element
/** * Creates child {@code <locale>} element that specifies a Java locale, * or set of locales, to include from the {@code jdk.localedata} module * in the linked image. * * @return new, unconfigured child element */
public LocaleSpec createLocale() { LocaleSpec locale = new LocaleSpec(); locales.add(locale); return locale; }
Sets attribute containing a list of locale patterns, to specify Java locales to include from jdk.localedata module in linked image. Asterisks (*) are permitted for wildcard matches.
Params:
  • localeList – comma-separated list of locale patterns
/** * Sets attribute containing a list of locale patterns, to specify * Java locales to include from {@code jdk.localedata} module in * linked image. Asterisks ({@code *}) are permitted for wildcard * matches. * * @param localeList comma-separated list of locale patterns */
public void setLocales(final String localeList) { for (String localeName : localeList.split(",")) { locales.add(new LocaleSpec(localeName)); } }
Creates child <excludeFiles> element that specifies files to exclude from linked modules when assembling linked image.
See Also:
Returns:new, unconfigured child element
/** * Creates child {@code <excludeFiles>} element that specifies * files to exclude from linked modules when assembling linked image. * * @return new, unconfigured child element * * @see #setExcludeFiles(String) */
public PatternListEntry createExcludeFiles() { PatternListEntry entry = new PatternListEntry(); excludedFiles.add(entry); return entry; }
Sets attribute containing a list of patterns denoting files to exclude from linked modules when assembling linked image.
Params:
  • patternList – comman-separated list of patterns
See Also:
/** * Sets attribute containing a list of patterns denoting files * to exclude from linked modules when assembling linked image. * * @param patternList comman-separated list of patterns * * @see Link.PatternListEntry */
public void setExcludeFiles(String patternList) { for (String pattern : patternList.split(",")) { excludedFiles.add(new PatternListEntry(pattern)); } }
Creates child <excludeResources> element that specifies resources in linked modules that will be excluded from linked image.
See Also:
Returns:new, unconfigured child element
/** * Creates child {@code <excludeResources>} element that specifies * resources in linked modules that will be excluded from linked image. * * @return new, unconfigured child element * * @see #setExcludeResources(String) */
public PatternListEntry createExcludeResources() { PatternListEntry entry = new PatternListEntry(); excludedResources.add(entry); return entry; }
Sets attribute containing a list of patterns denoting resources to exclude from linked modules in linked image.
Params:
  • patternList – comma-separated list of patterns
See Also:
/** * Sets attribute containing a list of patterns denoting resources * to exclude from linked modules in linked image. * * @param patternList comma-separated list of patterns * * @see #createExcludeResources() * @see Link.PatternListEntry */
public void setExcludeResources(String patternList) { for (String pattern : patternList.split(",")) { excludedResources.add(new PatternListEntry(pattern)); } }
Creates child <resourceOrder element that specifies explicit ordering of resources in linked image.
See Also:
Returns:new, unconfigured child element
/** * Creates child {@code <resourceOrder} element that specifies * explicit ordering of resources in linked image. * * @return new, unconfigured child element * * @see #setResourceOrder(String) */
public PatternListEntry createResourceOrder() { PatternListEntry order = new PatternListEntry(); ordering.add(order); return order; }
Sets attribute containing a list of patterns that explicitly order resources in the linked image. Any patterns specified here will be placed before any patterns specified as child elements.
Params:
  • patternList – comma-separated list of patterns
See Also:
/** * Sets attribute containing a list of patterns that explicitly * order resources in the linked image. Any patterns specified here * will be placed before any patterns specified as * {@linkplain #createResourceOrder() child elements}. * * @param patternList comma-separated list of patterns * * @see #createResourceOrder() * @see Link.PatternListEntry */
public void setResourceOrder(final String patternList) { List<PatternListEntry> orderList = new ArrayList<>(); for (String pattern : patternList.split(",")) { orderList.add(new PatternListEntry(pattern)); } // Attribute value comes before nested elements. ordering.addAll(0, orderList); }
Attribute indicating whether linked image should pull in providers in the module path of services used by explicitly linked modules.
See Also:
Returns:true if linked will pull in service provides, false if not
/** * Attribute indicating whether linked image should pull in providers * in the module path of services used by explicitly linked modules. * * @return true if linked will pull in service provides, false if not * * @see #setBindServices(boolean) */
public boolean getBindServices() { return bindServices; }
Sets attribute indicating whether linked image should pull in providers in the module path of services used by explicitly linked modules.
Params:
  • bind – whether to include service providers
See Also:
/** * Sets attribute indicating whether linked image should pull in providers * in the module path of services used by explicitly linked modules. * * @param bind whether to include service providers * * @see #getBindServices() */
public void setBindServices(final boolean bind) { this.bindServices = bind; }
Attribute indicating whether linker should allow modules made from signed jars.
See Also:
Returns:true if signed jars are allowed, false if modules based on signed jars cause an error
/** * Attribute indicating whether linker should allow modules made from * signed jars. * * @return true if signed jars are allowed, false if modules based on * signed jars cause an error * * @see #setIgnoreSigning(boolean) */
public boolean getIgnoreSigning() { return ignoreSigning; }
Sets attribute indicating whether linker should allow modules made from signed jars.

Note: As of Java 11, this attribute is internally forced to true. See the source.

Params:
  • ignore – true to have linker allow signed jars, false to have linker emit an error for signed jars
See Also:
/** * Sets attribute indicating whether linker should allow modules made from * signed jars. * <p> * Note: As of Java 11, this attribute is internally forced to true. See * <a href="https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java#L80">the source</a>. * * @param ignore true to have linker allow signed jars, * false to have linker emit an error for signed jars * * * @see #getIgnoreSigning() */
public void setIgnoreSigning(final boolean ignore) { this.ignoreSigning = ignore; }
Attribute indicating whether to include header files from linked modules in image.
See Also:
Returns:true if header files should be included, false to exclude them
/** * Attribute indicating whether to include header files from linked modules * in image. * * @return true if header files should be included, false to exclude them * * @see #setIncludeHeaders(boolean) */
public boolean getIncludeHeaders() { return includeHeaders; }
Sets attribute indicating whether to include header files from linked modules in image.
Params:
  • include – true if header files should be included, false to exclude them
See Also:
/** * Sets attribute indicating whether to include header files from * linked modules in image. * * @param include true if header files should be included, * false to exclude them * * @see #getIncludeHeaders() */
public void setIncludeHeaders(final boolean include) { this.includeHeaders = include; }
Attribute indicating whether to include man pages from linked modules in image.
See Also:
Returns:true if man pages should be included, false to exclude them
/** * Attribute indicating whether to include man pages from linked modules * in image. * * @return true if man pages should be included, false to exclude them * * @see #setIncludeManPages(boolean) */
public boolean getIncludeManPages() { return includeManPages; }
Sets attribute indicating whether to include man pages from linked modules in image.
Params:
  • include – true if man pages should be included, false to exclude them
See Also:
/** * Sets attribute indicating whether to include man pages from * linked modules in image. * * @param include true if man pages should be included, * false to exclude them * * @see #getIncludeManPages() */
public void setIncludeManPages(final boolean include) { this.includeManPages = include; }
Attribute indicating whether to include generated native commands, and native commands from linked modules, in image.
See Also:
Returns:true if native commands should be included, false to exclude them
/** * Attribute indicating whether to include generated native commands, * and native commands from linked modules, in image. * * @return true if native commands should be included, false to exclude them * * @see #setIncludeNativeCommands(boolean) */
public boolean getIncludeNativeCommands() { return includeNativeCommands; }
Sets attribute indicating whether to include generated native commands, and native commands from linked modules, in image.
Params:
  • include – true if native commands should be included, false to exclude them
See Also:
/** * Sets attribute indicating whether to include generated native commands, * and native commands from linked modules, in image. * * @param include true if native commands should be included, * false to exclude them * * @see #getIncludeNativeCommands() */
public void setIncludeNativeCommands(final boolean include) { this.includeNativeCommands = include; }
Attribute indicating whether linker should keep or strip debug information in classes.
See Also:
Returns:true if debug information will be retained, false if it will be stripped
/** * Attribute indicating whether linker should keep or strip * debug information in classes. * * @return true if debug information will be retained, * false if it will be stripped * * @see #setDebug(boolean) */
public boolean getDebug() { return debug; }
Sets attribute indicating whether linker should keep or strip debug information in classes.
Params:
  • debug – true if debug information should be retained, false if it should be stripped
See Also:
/** * Sets attribute indicating whether linker should keep or strip * debug information in classes. * * @param debug true if debug information should be retained, * false if it should be stripped * * @see #getDebug() */
public void setDebug(final boolean debug) { this.debug = debug; }
Attribute indicating whether linker should produce verbose output, and at what logging level that output should be shown.
See Also:
Returns:logging level at which to show linker's verbose output, or null to disable verbose output
/** * Attribute indicating whether linker should produce verbose output, * and at what logging level that output should be shown. * * @return logging level at which to show linker's verbose output, * or {@code null} to disable verbose output * * @see #setVerboseLevel(LogLevel) */
public LogLevel getVerboseLevel() { return verboseLevel; }
Sets attribute indicating whether linker should produce verbose output, and at what logging level that output should be shown.
Params:
  • level – level logging level at which to show linker's verbose output, or null to disable verbose output
See Also:
/** * Sets attribute indicating whether linker should produce verbose output, * and at what logging level that output should be shown. * * @param level level logging level at which to show linker's * verbose output, or {@code null} to disable verbose output * * @see #getVerboseLevel() */
public void setVerboseLevel(final LogLevel level) { this.verboseLevel = level; }
Required attribute containing directory where linked image will be created.
See Also:
Returns:directory where linked image will reside
/** * Required attribute containing directory where linked image will be * created. * * @return directory where linked image will reside * * @see #setDestDir(File) */
public File getDestDir() { return outputDir; }
Sets attribute indicating directory where linked image will be created.
Params:
  • dir – directory in which image will be created by linker
See Also:
/** * Sets attribute indicating directory where linked image will be created. * * @param dir directory in which image will be created by linker * * @see #getDestDir() */
public void setDestDir(final File dir) { this.outputDir = dir; }
Attribute indicating level of compression linker will apply to image. This is exclusive with regard to createCompress(): only one of the two may be specified.
See Also:
Returns:compression level to apply, or null for none
/** * Attribute indicating level of compression linker will apply to image. * This is exclusive with regard to {@link #createCompress()}: only one * of the two may be specified. * * @return compression level to apply, or {@code null} for none * * @see #setCompress(Link.CompressionLevel) * @see #createCompress() */
public CompressionLevel getCompress() { return compressionLevel; }
Sets attribute indicating level of compression linker will apply to image. This is exclusive with regard to createCompress(): only one of the two may be specified.
Params:
  • level – compression level to apply, or null for none
See Also:
/** * Sets attribute indicating level of compression linker will apply * to image. This is exclusive with regard to {@link #createCompress()}: * only one of the two may be specified. * * @param level compression level to apply, or {@code null} for none * * @see #getCompress() * @see #createCompress() */
public void setCompress(final CompressionLevel level) { this.compressionLevel = level; }
Creates child <compress> element that specifies the level of compression the linker will apply, and optionally, which files in the image will be compressed. This is exclusive with regard to the compress attribute: only one of the two may be specified.
See Also:
Returns:new, unconfigured child element
/** * Creates child {@code <compress>} element that specifies the level of * compression the linker will apply, and optionally, which files in the * image will be compressed. This is exclusive with regard to the * {@link #setCompress compress} attribute: only one of the two may be * specified. * * @return new, unconfigured child element * * @see #setCompress(Link.CompressionLevel) */
public Compression createCompress() { if (compression != null) { throw new BuildException( "Only one nested compression element is permitted.", getLocation()); } compression = new Compression(); return compression; }
Attribute which indicates whether certain files in the linked image will be big-endian or little-endian. If null, the underlying platform's endianness is used.
See Also:
Returns:endianness to apply, or null to platform default
/** * Attribute which indicates whether certain files in the linked image * will be big-endian or little-endian. If {@code null}, the underlying * platform's endianness is used. * * @return endianness to apply, or {@code null} to platform default * * @see #setEndianness(Link.Endianness) */
public Endianness getEndianness() { return endianness; }
Sets attribute which indicates whether certain files in the linked image will be big-endian or little-endian. If null, the underlying platform's endianness is used.
Params:
  • endianness – endianness to apply, or null to use platform default
See Also:
/** * Sets attribute which indicates whether certain files in the linked image * will be big-endian or little-endian. If {@code null}, the underlying * platform's endianness is used. * * @param endianness endianness to apply, or {@code null} to use * platform default * * @see #getEndianness() */
public void setEndianness(final Endianness endianness) { this.endianness = endianness; }
Attribute indicating whether linker should check legal notices with duplicate names, and refuse to merge them (usually using symbolic links) if their respective content is not identical.
See Also:
Returns:true if legal notice files with same name should be checked for identical content, false to suppress check
/** * Attribute indicating whether linker should check legal notices with * duplicate names, and refuse to merge them (usually using symbolic links) * if their respective content is not identical. * * @return true if legal notice files with same name should be checked * for identical content, false to suppress check * * @see #setCheckDuplicateLegal(boolean) */
public boolean getCheckDuplicateLegal() { return checkDuplicateLegal; }
Sets attribute indicating whether linker should check legal notices with duplicate names, and refuse to merge them (usually using symbolic links) if their respective content is not identical.
Params:
  • check – true if legal notice files with same name should be checked for identical content, false to suppress check
See Also:
/** * Sets attribute indicating whether linker should check legal notices with * duplicate names, and refuse to merge them (usually using symbolic links) * if their respective content is not identical. * * @param check true if legal notice files with same name should be checked * for identical content, false to suppress check * * @see #getCheckDuplicateLegal() */
public void setCheckDuplicateLegal(final boolean check) { this.checkDuplicateLegal = check; }
Attribute indicating what type of JVM the linked image should have. If null, all JVM types are included.
See Also:
Returns:type of JVM linked image will have
/** * Attribute indicating what type of JVM the linked image should have. * If {@code null}, all JVM types are included. * * @return type of JVM linked image will have * * @see #setVmType(Link.VMType) */
public VMType getVmType() { return vmType; }
Set attribute indicating what type of JVM the linked image should have. If null, all JVM types are included.
Params:
  • type – type of JVM linked image will have
See Also:
/** * Set attribute indicating what type of JVM the linked image should have. * If {@code null}, all JVM types are included. * * @param type type of JVM linked image will have * * @see #getVmType() */
public void setVmType(final VMType type) { this.vmType = type; }
Creates child <releaseInfo> element that modifies the default release properties of the linked image.
Returns:new, unconfigured child element
/** * Creates child {@code <releaseInfo>} element that modifies the default * release properties of the linked image. * * @return new, unconfigured child element */
public ReleaseInfo createReleaseInfo() { ReleaseInfo info = new ReleaseInfo(); releaseInfo.add(info); return info; }
Child element that explicitly names a Java module.
/** * Child element that explicitly names a Java module. */
public class ModuleSpec {
Module's name. Required.
/** Module's name. Required. */
private String name;
Creates an unconfigured element.
/** Creates an unconfigured element. */
public ModuleSpec() { // Deliberately empty. }
Creates an element with the given module name.
Params:
  • name – module's name
/** * Creates an element with the given module name. * * @param name module's name */
public ModuleSpec(final String name) { setName(name); }
Attribute containing name of module this element represents.
Returns:name of module
/** * Attribute containing name of module this element represents. * * @return name of module */
public String getName() { return name; }
Sets attribute representing the name of this module this element represents.
Params:
  • name – module's name
/** * Sets attribute representing the name of this module this element * represents. * * @param name module's name */
public void setName(final String name) { this.name = name; }
Verifies this element's state.
Throws:
  • BuildException – if name is not set
/** * Verifies this element's state. * * @throws BuildException if name is not set */
public void validate() { if (name == null) { throw new BuildException("name is required for module.", getLocation()); } } }
Child element that contains a pattern matching Java locales.
/** * Child element that contains a pattern matching Java locales. */
public class LocaleSpec {
Pattern of locale names to match.
/** Pattern of locale names to match. */
private String name;
Creates an unconfigured element.
/** Creates an unconfigured element. */
public LocaleSpec() { // Deliberately empty. }
Creates an element with the given name pattern.
Params:
  • name – pattern of locale names to match
/** * Creates an element with the given name pattern. * * @param name pattern of locale names to match */
public LocaleSpec(final String name) { setName(name); }
Attribute containing a pattern which matches Java locale names. May be an explicit Java locale, or may contain an asterisk (*) for wildcard matching.
Returns:this element's locale name pattern
/** * Attribute containing a pattern which matches Java locale names. * May be an explicit Java locale, or may contain an asterisk * ({@code *)} for wildcard matching. * * @return this element's locale name pattern */
public String getName() { return name; }
Sets attribute containing a pattern which matches Java locale names. May be an explicit Java locale, or may contain an asterisk (*) for wildcard matching.
Params:
  • name – new locale name or pattern matching locale names
/** * Sets attribute containing a pattern which matches Java locale names. * May be an explicit Java locale, or may contain an asterisk * ({@code *)} for wildcard matching. * * @param name new locale name or pattern matching locale names */
public void setName(final String name) { this.name = name; }
Verifies this element's state.
Throws:
  • BuildException – if name is not set
/** * Verifies this element's state. * * @throws BuildException if name is not set */
public void validate() { if (name == null) { throw new BuildException("name is required for locale.", getLocation()); } } }
Child element type which specifies a jlink files pattern. Each instance may specify a string PathMatcher pattern or a text file containing a list of such patterns, one per line.
/** * Child element type which specifies a jlink files pattern. Each * instance may specify a string * <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher%28java.lang.String%29">PathMatcher pattern</a> * or a text file containing a list of such patterns, one per line. */
public class PatternListEntry {
PathMatcher pattern of files to match.
/** PathMatcher pattern of files to match. */
private String pattern;
Plain text list file with one PathMatcher pattern per line.
/** Plain text list file with one PathMatcher pattern per line. */
private File file;
Creates an unconfigured element.
/** Creates an unconfigured element. */
public PatternListEntry() { // Deliberately empty. }
Creates a new element from either a pattern or listing file. If the argument starts with "@", the remainder of it is assumed to be a listing file; otherwise, it is treated as a PathMatcher pattern.
Params:
  • pattern – a PathMatcher pattern or @-filename
/** * Creates a new element from either a pattern or listing file. * If the argument starts with "{@code @}", the remainder of it * is assumed to be a listing file; otherwise, it is treated as * a PathMatcher pattern. * * @param pattern a PathMatcher pattern or {@code @}-filename */
public PatternListEntry(final String pattern) { if (pattern.startsWith("@")) { setListFile(new File(pattern.substring(1))); } else { setPattern(pattern); } }
Returns this element's PathMatcher pattern attribute, if set.
Returns:this element's files pattern
/** * Returns this element's PathMatcher pattern attribute, if set. * * @return this element's files pattern */
public String getPattern() { return pattern; }
Sets this element's PathMatcher pattern attribute for matching files.
Params:
  • pattern – new files pattern
/** * Sets this element's * <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher%28java.lang.String%29">PathMatcher pattern</a> * attribute for matching files. * * @param pattern new files pattern */
public void setPattern(final String pattern) { this.pattern = pattern; }
Returns this element's list file attribute, if set.
See Also:
Returns:this element's list file
/** * Returns this element's list file attribute, if set. * * @return this element's list file * * @see #setListFile(File) */
public File getListFile() { return file; }
Sets this element's list file attribute. The file must be a plain text file with one PathMatcher pattern per line.
Params:
  • file – list file containing patterns
See Also:
/** * Sets this element's list file attribute. The file must be a * plain text file with one PathMatcher pattern per line. * * @param file list file containing patterns * * @see #getListFile() */
public void setListFile(final File file) { this.file = file; }
Verifies this element's state.
Throws:
  • BuildException – if both pattern and file are set
  • BuildException – if neither pattern nor file is set
/** * Verifies this element's state. * * @throws BuildException if both pattern and file are set * @throws BuildException if neither pattern nor file is set */
public void validate() { if ((pattern == null && file == null) || (pattern != null && file != null)) { throw new BuildException( "Each entry in a pattern list must specify " + "exactly one of pattern or file.", getLocation()); } }
Converts this element to a jlink command line attribute, either this element's bare pattern, or its list file preceded by "@".
Returns:this element's information converted to a command line value
/** * Converts this element to a jlink command line attribute, * either this element's bare pattern, or its list file * preceded by "{@code @}". * * @return this element's information converted to a command line value */
public String toOptionValue() { return pattern != null ? pattern : ("@" + file); } }
Child element representing a custom launcher command in a linked image. A launcher has a name, which is typically used as a file name for an executable file, a Java module name, and optionally a class within that module which can act as a standard Java main class.
/** * Child element representing a custom launcher command in a linked image. * A launcher has a name, which is typically used as a file name for an * executable file, a Java module name, and optionally a class within * that module which can act as a standard Java main class. */
public class Launcher {
This launcher's name, usually used to create an executable file.
/** This launcher's name, usually used to create an executable file. */
private String name;
The name of the Java module this launcher launches.
/** The name of the Java module this launcher launches. */
private String module;
The class within this element's module to run. Optional if the Java module specifies its own main class.
/** * The class within this element's {@link #module} to run. * Optional if the Java module specifies its own main class. */
private String mainClass;
Creates a new, unconfigured element.
/** Creates a new, unconfigured element. */
public Launcher() { // Deliberately empty. }
Creates a new element from a jlink-compatible string specifier, which must take the form name=module or name=module/mainclass.
Params:
  • textSpec – name, module, and optional main class, as described above
Throws:
/** * Creates a new element from a {@code jlink}-compatible string * specifier, which must take the form * <var>name</var>{@code =}<var>module</var> or * <var>name</var>{@code =}<var>module</var>{@code /}<var>mainclass</var>. * * @param textSpec name, module, and optional main class, as described * above * * @throws NullPointerException if argument is {@code null} * @throws BuildException if argument does not conform to above * requirements */
public Launcher(final String textSpec) { Objects.requireNonNull(textSpec, "Text cannot be null"); int equals = textSpec.lastIndexOf('='); if (equals < 1) { throw new BuildException(INVALID_LAUNCHER_STRING); } setName(textSpec.substring(0, equals)); int slash = textSpec.indexOf('/', equals); if (slash < 0) { setModule(textSpec.substring(equals + 1)); } else if (slash > equals + 1 && slash < textSpec.length() - 1) { setModule(textSpec.substring(equals + 1, slash)); setMainClass(textSpec.substring(slash + 1)); } else { throw new BuildException(INVALID_LAUNCHER_STRING); } }
Returns this element's name attribute, typically used as the basis of an executable file name.
See Also:
Returns:this element's name
/** * Returns this element's name attribute, typically used as the basis * of an executable file name. * * @return this element's name * * @see #setName(String) */
public String getName() { return name; }
Sets this element's name attribute, which is typically used by the linker to create an executable file with a similar name. Thus, the name should contain only characters safe for file names.
Params:
  • name – name of launcher
/** * Sets this element's name attribute, which is typically used by the * linker to create an executable file with a similar name. Thus, * the name should contain only characters safe for file names. * * @param name name of launcher */
public void setName(final String name) { this.name = name; }
Returns the attribute of this element which contains the name of the Java module to execute.
Returns:this element's module name
/** * Returns the attribute of this element which contains the * name of the Java module to execute. * * @return this element's module name */
public String getModule() { return module; }
Sets the attribute of this element which contains the name of a Java module to execute.
Params:
  • module – name of module to execute
/** * Sets the attribute of this element which contains the name of * a Java module to execute. * * @param module name of module to execute */
public void setModule(final String module) { this.module = module; }
Returns the attribute of this element which contains the main class to execute in this element's module, if that module doesn't define its main class.
Returns:name of main class to execute
/** * Returns the attribute of this element which contains the main class * to execute in this element's {@linkplain #getModule() module}, if * that module doesn't define its main class. * * @return name of main class to execute */
public String getMainClass() { return mainClass; }
Sets the attribute which contains the main class to execute in this element's module, if that module doesn't define its main class.
Params:
  • className – name of class to execute
/** * Sets the attribute which contains the main class to execute in * this element's {@linkplain #getModule() module}, if that module * doesn't define its main class. * * @param className name of class to execute */
public void setMainClass(final String className) { this.mainClass = className; }
Verifies this element's state.
Throws:
  • BuildException – if name or module is not set
/** * Verifies this element's state. * * @throws BuildException if name or module is not set */
public void validate() { if (name == null || name.isEmpty()) { throw new BuildException("Launcher must have a name", getLocation()); } if (module == null || module.isEmpty()) { throw new BuildException("Launcher must have specify a module", getLocation()); } }
Returns this element's information in jlink launcher format: name=module or name=module/mainclass.
Returns:name, module and optional main class in jlink format
/** * Returns this element's information in jlink launcher format: * <var>name</var>{@code =}<var>module</var> or * <var>name</var>{@code =}<var>module</var>{@code /}<var>mainclass</var>. * * @return name, module and optional main class in jlink format */
@Override public String toString() { if (mainClass != null) { return name + "=" + module + "/" + mainClass; } else { return name + "=" + module; } } }
Possible values for linked image endianness: little and big.
/** * Possible values for linked image endianness: * {@code little} and {@code big}. */
public static class Endianness extends EnumeratedAttribute { @Override public String[] getValues() { return new String[] { "little", "big" }; } }
Possible values for JVM type in linked image: client, server, minimal, or all.
/** * Possible values for JVM type in linked image: * {@code client}, {@code server}, {@code minimal}, or {@code all}. */
public static class VMType extends EnumeratedAttribute { @Override public String[] getValues() { return new String[] { "client", "server", "minimal", "all" }; } }
Possible attribute values for compression level of a linked image:
0
none
no compression (default)
1
strings
constant string sharing
2
zip
zip compression
/** * Possible attribute values for compression level of a linked image: * <dl> * <dt>{@code 0} * <dt>{@code none} * <dd>no compression (default) * <dt>{@code 1} * <dt>{@code strings} * <dd>constant string sharing * <dt>{@code 2} * <dt>{@code zip} * <dd>zip compression * </dl> */
public static class CompressionLevel extends EnumeratedAttribute { private static final Map<String, String> KEYWORDS; static { Map<String, String> map = new LinkedHashMap<>(); map.put("0", "0"); map.put("1", "1"); map.put("2", "2"); map.put("none", "0"); map.put("strings", "1"); map.put("zip", "2"); KEYWORDS = Collections.unmodifiableMap(map); } @Override public String[] getValues() { return KEYWORDS.keySet().toArray(new String[0]); }
Converts this value to a string suitable for use in a jlink command.
Returns:jlink keyword corresponding to this value
/** * Converts this value to a string suitable for use in a * jlink command. * * @return jlink keyword corresponding to this value */
String toCommandLineOption() { return KEYWORDS.get(getValue()); } }
Child element fully describing compression of a linked image. This includes the level, and optionally, the names of files to compress.
/** * Child element fully describing compression of a linked image. * This includes the level, and optionally, the names of files to compress. */
public class Compression {
Compression level. Required attribute.
/** Compression level. Required attribute. */
private CompressionLevel level;
Patterns specifying files to compress. If empty, all files are compressed.
/** * Patterns specifying files to compress. If empty, all files are * compressed. */
private final List<PatternListEntry> patterns = new ArrayList<>();
Required attribute containing level of compression.
Returns:compression level
/** * Required attribute containing level of compression. * * @return compression level */
public CompressionLevel getLevel() { return level; }
Sets attribute indicating level of compression.
Params:
  • level – type of compression to apply to linked image
/** * Sets attribute indicating level of compression. * * @param level type of compression to apply to linked image */
public void setLevel(final CompressionLevel level) { this.level = level; }
Creates a nested element which can specify a pattern of files to compress.
Returns:new, unconfigured child element
/** * Creates a nested element which can specify a pattern of files * to compress. * * @return new, unconfigured child element */
public PatternListEntry createFiles() { PatternListEntry pattern = new PatternListEntry(); patterns.add(pattern); return pattern; }
Sets an attribute that represents a list of file patterns to compress in the linked image, as a comma-separated list of PathMatcher patterns or pattern list files.
Params:
  • patternList – comma-separated list of patterns and/or file names
See Also:
/** * Sets an attribute that represents a list of file patterns to * compress in the linked image, as a comma-separated list of * PathMatcher patterns or pattern list files. * * @param patternList comma-separated list of patterns and/or file names * * @see Link.PatternListEntry */
public void setFiles(final String patternList) { patterns.clear(); for (String pattern : patternList.split(",")) { patterns.add(new PatternListEntry(pattern)); } }
Verifies this element's state.
Throws:
  • BuildException – if compression level is not set
  • BuildException – if any nested patterns are invalid
/** * Verifies this element's state. * * @throws BuildException if compression level is not set * @throws BuildException if any nested patterns are invalid */
public void validate() { if (level == null) { throw new BuildException("Compression level must be specified.", getLocation()); } patterns.forEach(PatternListEntry::validate); }
Converts this element to a single jlink option value.
Returns:command line option representing this element's state
/** * Converts this element to a single jlink option value. * * @return command line option representing this element's state */
public String toCommandLineOption() { StringBuilder option = new StringBuilder(level.toCommandLineOption()); if (!patterns.isEmpty()) { String separator = ":filter="; for (PatternListEntry entry : patterns) { option.append(separator).append(entry.toOptionValue()); separator = ","; } } return option.toString(); } }
Grandchild element representing deletable key in a linked image's release properties.
/** * Grandchild element representing deletable key in a linked image's * release properties. */
public class ReleaseInfoKey {
Required attribute holding property key to delete.
/** Required attribute holding property key to delete. */
private String key;
Creates a new, unconfigured element.
/** Creates a new, unconfigured element. */
public ReleaseInfoKey() { // Deliberately empty. }
Creates a new element with the specified key.
Params:
  • key – property key to delete from release info
/** * Creates a new element with the specified key. * * @param key property key to delete from release info */
public ReleaseInfoKey(final String key) { setKey(key); }
Attribute holding the release info property key to delete.
Returns:property key to be deleted
/** * Attribute holding the release info property key to delete. * * @return property key to be deleted */
public String getKey() { return key; }
Sets attribute containing property key to delete from linked image's release info.
Params:
  • key – propert key to be deleted
/** * Sets attribute containing property key to delete from * linked image's release info. * * @param key propert key to be deleted */
public void setKey(final String key) { this.key = key; }
Verifies this element's state is valid.
Throws:
  • BuildException – if key is not set
/** * Verifies this element's state is valid. * * @throws BuildException if key is not set */
public void validate() { if (key == null) { throw new BuildException( "Release info key must define a 'key' attribute.", getLocation()); } } }
Grandchild element describing additional release info properties for a linked image. To be valid, an instance must have either a non-null key and value, or a non-null file.
/** * Grandchild element describing additional release info properties for a * linked image. To be valid, an instance must have either a non-null * key and value, or a non-null file. */
public class ReleaseInfoEntry {
New release property's key.
/** New release property's key. */
private String key;
New release property's value.
/** New release property's value. */
private String value;
File containing additional release properties.
/** File containing additional release properties. */
private File file;
Charset of file.
/** Charset of {@link #file}. */
private String charset = StandardCharsets.ISO_8859_1.name();
Creates a new, unconfigured element.
/** Creates a new, unconfigured element. */
public ReleaseInfoEntry() { // Deliberately empty. }
Creates a new element which specifies a single additional property.
Params:
  • key – new property's key
  • value – new property's value
/** * Creates a new element which specifies a single additional property. * * @param key new property's key * @param value new property's value */
public ReleaseInfoEntry(final String key, final String value) { setKey(key); setValue(value); }
Attribute containing the key of this element's additional property.
See Also:
Returns:additional property's key
/** * Attribute containing the key of this element's additional property. * * @return additional property's key * * @see #getValue() */
public String getKey() { return key; }
Sets attribute containing the key of this element's additional property.
Params:
  • key – additional property's key
See Also:
/** * Sets attribute containing the key of this element's * additional property. * * @param key additional property's key * * @see #setValue(String) */
public void setKey(final String key) { this.key = key; }
Attribute containing the value of this element's additional property.
See Also:
Returns:additional property's value
/** * Attribute containing the value of this element's additional property. * * @return additional property's value * * @see #getKey() */
public String getValue() { return value; }
Sets attributes containing the value of this element's additional property.
Params:
  • value – additional property's value
See Also:
/** * Sets attributes containing the value of this element's * additional property. * * @param value additional property's value * * @see #setKey(String) */
public void setValue(final String value) { this.value = value; }
Attribute containing a Java properties file which contains additional release info properties. This is exclusive with respect to the key and value of this instance: either the file must be set, or the key and value must be set.
Returns:this element's properties file
/** * Attribute containing a Java properties file which contains * additional release info properties. This is exclusive with * respect to the {@linkplain #getKey() key} and * {@linkplain #getValue() value} of this instance: either the * file must be set, or the key and value must be set. * * @return this element's properties file */
public File getFile() { return file; }
Sets attribute containing a Java properties file which contains additional release info properties. This is exclusive with respect to the key and value of this instance: either the file must be set, or the key and value must be set.
Params:
  • file – this element's properties file
/** * Sets attribute containing a Java properties file which contains * additional release info properties. This is exclusive with * respect to the {@linkplain #setKey(String) key} and * {@linkplain #setValue(String) value} of this instance: either the * file must be set, or the key and value must be set. * * @param file this element's properties file */
public void setFile(final File file) { this.file = file; }
Attribute containing the character set of this object's file. This is ISO_8859_1 by default, in accordance with the java.util.Properties default.
Returns:character set of this element's file
/** * Attribute containing the character set of this object's * {@linkplain #getFile() file}. This is {@code ISO_8859_1} * by default, in accordance with the java.util.Properties default. * * @return character set of this element's file */
public String getCharset() { return charset; }
Sets attribute containing the character set of this object's file. If not set, this is ISO_8859_1 by default, in accordance with the java.util.Properties default.
Params:
  • charset – character set of this element's file
/** * Sets attribute containing the character set of this object's * {@linkplain #setFile(File) file}. If not set, this is * {@code ISO_8859_1} by default, in accordance with the * java.util.Properties default. * * @param charset character set of this element's file */
public void setCharset(final String charset) { this.charset = charset; }
Verifies the state of this element.
Throws:
  • BuildException – if file is set, and key and/or value are set
  • BuildException – if file is not set, and key and value are not both set
  • BuildException – if charset is not a valid Java Charset name
/** * Verifies the state of this element. * * @throws BuildException if file is set, and key and/or value are set * @throws BuildException if file is not set, and key and value are not both set * @throws BuildException if charset is not a valid Java Charset name */
public void validate() { if (file == null && (key == null || value == null)) { throw new BuildException( "Release info must define 'key' and 'value' attributes, " + "or a 'file' attribute.", getLocation()); } if (file != null && (key != null || value != null)) { throw new BuildException( "Release info cannot define both a file attribute and " + "key/value attributes.", getLocation()); } // This can't happen from a build file, but can theoretically // happen if called from Java code. if (charset == null) { throw new BuildException("Charset cannot be null.", getLocation()); } try { Charset.forName(charset); } catch (IllegalArgumentException e) { throw new BuildException(e, getLocation()); } }
Converts this element to a Java properties object containing the additional properties this element represents. If this element's file is set, it is read; otherwise, a Properties object containing just one property, consisting of this element's key and value, is returned.
Throws:
Returns:new Properties object obtained from this element's file or its key and value
/** * Converts this element to a Java properties object containing * the additional properties this element represents. If this * element's file is set, it is read; otherwise, a Properties * object containing just one property, consisting of this element's * key and value, is returned. * * @return new Properties object obtained from this element's file or * its key and value * * @throws BuildException if file is set, but cannot be read */
public Properties toProperties() { Properties props = new Properties(); if (file != null) { try (Reader reader = Files.newBufferedReader( file.toPath(), Charset.forName(charset))) { props.load(reader); } catch (IOException e) { throw new BuildException( "Cannot read release info file \"" + file + "\": " + e, e, getLocation()); } } else { props.setProperty(key, value); } return props; } }
Child element describing changes to the default release properties of a linked image.
/** * Child element describing changes to the default release properties * of a linked image. */
public class ReleaseInfo {
File that contains replacement release properties for linked image.
/** * File that contains replacement release properties for linked image. */
private File file;
Properties to add to default release properties of linked image.
/** * Properties to add to default release properties of linked image. */
private final List<ReleaseInfoEntry> propertiesToAdd = new ArrayList<>();
Property keys to remove from release properties of linked image.
/** * Property keys to remove from release properties of linked image. */
private final List<ReleaseInfoKey> propertiesToDelete = new ArrayList<>();
Attribute specifying Java properties file which will replace the default release info properties for the linked image.
Returns:release properties file
/** * Attribute specifying Java properties file which will replace the * default release info properties for the linked image. * * @return release properties file */
public File getFile() { return file; }
Sets attribute specifying Java properties file which will replace the default release info properties for the linked image.
Params:
  • file – replacement release properties file
/** * Sets attribute specifying Java properties file which will replace * the default release info properties for the linked image. * * @param file replacement release properties file */
public void setFile(final File file) { this.file = file; }
Creates an uninitialized child element which can represent properties to add to the default release properties of a linked image.
Returns:new, unconfigured child element
/** * Creates an uninitialized child element which can represent properties * to add to the default release properties of a linked image. * * @return new, unconfigured child element */
public ReleaseInfoEntry createAdd() { ReleaseInfoEntry property = new ReleaseInfoEntry(); propertiesToAdd.add(property); return property; }
Creates an uninitialized child element which can represent a property key to delete from the release properties of a linked image.
Returns:new, unconfigured child element
/** * Creates an uninitialized child element which can represent * a property key to delete from the release properties of * a linked image. * * @return new, unconfigured child element */
public ReleaseInfoKey createDelete() { ReleaseInfoKey key = new ReleaseInfoKey(); propertiesToDelete.add(key); return key; }
Sets attribute which contains a comma-separated list of property keys to delete from the release properties of a linked image.
Params:
  • keyList – comma-separated list of property keys
See Also:
/** * Sets attribute which contains a comma-separated list of * property keys to delete from the release properties of * a linked image. * * @param keyList comma-separated list of property keys * * @see #createDelete() */
public void setDelete(final String keyList) { for (String key : keyList.split(",")) { propertiesToDelete.add(new ReleaseInfoKey(key)); } }
Verifies the state of this element.
Throws:
  • BuildException – if any child element is invalid
See Also:
/** * Verifies the state of this element. * * @throws BuildException if any child element is invalid * * @see Link.ReleaseInfoEntry#validate() * @see Link.ReleaseInfoKey#validate() */
public void validate() { propertiesToAdd.forEach(ReleaseInfoEntry::validate); propertiesToDelete.forEach(ReleaseInfoKey::validate); }
Converts all of this element's state to a series of jlink options.
Returns:new collection of jlink options based on this element's attributes and child elements
/** * Converts all of this element's state to a series of * <code>jlink</code> options. * * @return new collection of jlink options based on this element's * attributes and child elements */
public Collection<String> toCommandLineOptions() { Collection<String> options = new ArrayList<>(); if (file != null) { options.add("--release-info=" + file); } if (!propertiesToAdd.isEmpty()) { StringBuilder option = new StringBuilder("--release-info=add"); for (ReleaseInfoEntry entry : propertiesToAdd) { Properties props = entry.toProperties(); for (String key : props.stringPropertyNames()) { option.append(":").append(key).append("="); option.append(props.getProperty(key)); } } options.add(option.toString()); } if (!propertiesToDelete.isEmpty()) { StringBuilder option = new StringBuilder("--release-info=del:keys="); String separator = ""; for (ReleaseInfoKey key : propertiesToDelete) { option.append(separator).append(key.getKey()); // jlink docs aren't clear on whether property keys // to delete should be separated by commas or colons. separator = ","; } options.add(option.toString()); } return options; } }
Invokes the jlink tool to create a new linked image, unless the output directory exists and all of its files are files are newer than all files in the module path.
Throws:
  • BuildException – if destDir is not set
  • BuildException – if module path is unset or empty
  • BuildException – if module list is empty
  • BuildException – if compressionLevel attribute and compression child element are both specified
/** * Invokes the jlink tool to create a new linked image, unless the * output directory exists and all of its files are files are newer * than all files in the module path. * * @throws BuildException if destDir is not set * @throws BuildException if module path is unset or empty * @throws BuildException if module list is empty * @throws BuildException if compressionLevel attribute and compression * child element are both specified */
@Override public void execute() throws BuildException { if (outputDir == null) { throw new BuildException("Destination directory is required.", getLocation()); } if (modulePath == null || modulePath.isEmpty()) { throw new BuildException("Module path is required.", getLocation()); } if (modules.isEmpty()) { throw new BuildException("At least one module must be specified.", getLocation()); } if (outputDir.exists()) { CompositeMapper imageMapper = new CompositeMapper(); try (Stream<java.nio.file.Path> imageTree = Files.walk(outputDir.toPath())) { /* * Is this sufficient? What if part of the image tree was * deleted or altered? Should we check for standard * files and directories, like 'bin', 'lib', 'conf', 'legal', * and 'release'? (Some, like 'include', may not be present, * if the image was previously built with options that * omitted them.) */ imageTree.forEach( p -> imageMapper.add(new MergingMapper(p.toString()))); ResourceCollection outOfDate = ResourceUtils.selectOutOfDateSources(this, modulePath, imageMapper, getProject(), FileUtils.getFileUtils().getFileTimestampGranularity()); if (outOfDate.isEmpty()) { log("Skipping image creation, since " + "\"" + outputDir + "\" is already newer than " + "all constituent modules.", Project.MSG_VERBOSE); return; } } catch (IOException e) { throw new BuildException( "Could not scan \"" + outputDir + "\" " + "for being up-to-date: " + e, e, getLocation()); } } modules.forEach(ModuleSpec::validate); observableModules.forEach(ModuleSpec::validate); launchers.forEach(Launcher::validate); locales.forEach(LocaleSpec::validate); ordering.forEach(PatternListEntry::validate); excludedFiles.forEach(PatternListEntry::validate); excludedResources.forEach(PatternListEntry::validate); Collection<String> args = buildJlinkArgs(); ToolProvider jlink = ToolProvider.findFirst("jlink").orElseThrow( () -> new BuildException("jlink tool not found in JDK.", getLocation())); if (outputDir.exists()) { log("Deleting existing " + outputDir, Project.MSG_VERBOSE); deleteTree(outputDir.toPath()); } log("Executing: jlink " + String.join(" ", args), Project.MSG_VERBOSE); ByteArrayOutputStream stdout = new ByteArrayOutputStream(); ByteArrayOutputStream stderr = new ByteArrayOutputStream(); int exitCode; try (PrintStream out = new PrintStream(stdout); PrintStream err = new PrintStream(stderr)) { exitCode = jlink.run(out, err, args.toArray(new String[0])); } if (exitCode != 0) { StringBuilder message = new StringBuilder(); message.append("jlink failed (exit code ").append(exitCode).append(")"); if (stdout.size() > 0) { message.append(", output is: ").append(stdout); } if (stderr.size() > 0) { message.append(", error output is: ").append(stderr); } throw new BuildException(message.toString(), getLocation()); } if (verboseLevel != null) { int level = verboseLevel.getLevel(); if (stdout.size() > 0) { log(stdout.toString(), level); } if (stderr.size() > 0) { log(stderr.toString(), level); } } log("Created " + outputDir.getAbsolutePath(), Project.MSG_INFO); }
Recursively deletes a file tree.
Params:
  • dir – root of tree to delete
Throws:
/** * Recursively deletes a file tree. * * @param dir root of tree to delete * * @throws BuildException if deletion fails */
private void deleteTree(java.nio.file.Path dir) { try { Files.walkFileTree(dir, new SimpleFileVisitor<java.nio.file.Path>() { @Override public FileVisitResult visitFile(final java.nio.file.Path file, final BasicFileAttributes attr) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(final java.nio.file.Path dir, IOException e) throws IOException { if (e == null) { Files.delete(dir); } return super.postVisitDirectory(dir, e); } }); } catch (IOException e) { throw new BuildException( "Could not delete \"" + dir + "\": " + e, e, getLocation()); } }
Creates list of arguments to jlink tool, based on this instance's current state.
Throws:
  • BuildException – if any inconsistencies attributes/elements is found
Returns:new list of jlink arguments
/** * Creates list of arguments to <code>jlink</code> tool, based on this * instance's current state. * * @return new list of <code>jlink</code> arguments * * @throws BuildException if any inconsistencies attributes/elements * is found */
private Collection<String> buildJlinkArgs() { Collection<String> args = new ArrayList<>(); args.add("--output"); args.add(outputDir.toString()); args.add("--module-path"); args.add(modulePath.toString()); args.add("--add-modules"); args.add(modules.stream().map(ModuleSpec::getName).collect( Collectors.joining(","))); if (!observableModules.isEmpty()) { args.add("--limit-modules"); args.add(observableModules.stream().map(ModuleSpec::getName).collect( Collectors.joining(","))); } if (!locales.isEmpty()) { args.add("--include-locales=" + locales.stream().map(LocaleSpec::getName).collect( Collectors.joining(","))); } for (Launcher launcher : launchers) { args.add("--launcher"); args.add(launcher.toString()); } if (!ordering.isEmpty()) { args.add("--order-resources=" + ordering.stream().map(PatternListEntry::toOptionValue).collect( Collectors.joining(","))); } if (!excludedFiles.isEmpty()) { args.add("--exclude-files=" + excludedFiles.stream().map(PatternListEntry::toOptionValue).collect( Collectors.joining(","))); } if (!excludedResources.isEmpty()) { args.add("--exclude-resources=" + excludedResources.stream().map(PatternListEntry::toOptionValue).collect( Collectors.joining(","))); } if (bindServices) { args.add("--bind-services"); } if (ignoreSigning) { args.add("--ignore-signing-information"); } if (!includeHeaders) { args.add("--no-header-files"); } if (!includeManPages) { args.add("--no-man-pages"); } if (!includeNativeCommands) { args.add("--strip-native-commands"); } if (!debug) { args.add("--strip-debug"); } if (verboseLevel != null) { args.add("--verbose"); } if (endianness != null) { args.add("--endian"); args.add(endianness.getValue()); } if (compressionLevel != null) { if (compression != null) { throw new BuildException("compressionLevel attribute " + "and <compression> child element cannot both be present.", getLocation()); } args.add("--compress=" + compressionLevel.toCommandLineOption()); } if (compression != null) { compression.validate(); args.add("--compress=" + compression.toCommandLineOption()); } if (vmType != null) { args.add("--vm=" + vmType.getValue()); } if (checkDuplicateLegal) { args.add("--dedup-legal-notices=error-if-not-same-content"); } for (ReleaseInfo info : releaseInfo) { info.validate(); args.addAll(info.toCommandLineOptions()); } return args; } }