This is an automated email from the ASF dual-hosted git repository. rfscholte pushed a commit to branch MJLINK-36 in repository https://gitbox.apache.org/repos/asf/maven-jlink-plugin.git
commit 6e0a0e91b73047927f9a5a8b1c43b31cd9ffb003 Author: Benjamin Marwell <[email protected]> AuthorDate: Fri Nov 13 21:23:30 2020 +0100 Java9 ToolProvider via multi-release jar. --- pom.xml | 47 ++++++ .../maven/plugins/jlink/AbstractJLinkExecutor.java | 72 +++++++++ .../maven/plugins/jlink/AbstractJLinkMojo.java | 149 +----------------- .../apache/maven/plugins/jlink/JLinkExecutor.java | 171 ++++++++++++++++++++ .../org/apache/maven/plugins/jlink/JLinkMojo.java | 67 +++----- .../apache/maven/plugins/jlink/JLinkExecutor.java | 175 +++++++++++++++++++++ 6 files changed, 493 insertions(+), 188 deletions(-) diff --git a/pom.xml b/pom.xml index da94e29..45e7acc 100644 --- a/pom.xml +++ b/pom.xml @@ -199,6 +199,53 @@ </reporting> <profiles> <profile> + <id>jdk9</id> + <activation> + <jdk>[9,)</jdk> + </activation> + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>jdk9</id> + <goals> + <goal>compile</goal> + </goals> + <configuration> + <release>9</release> + <multiReleaseOutput>true</multiReleaseOutput> + <compileSourceRoots> + <compileSourceRoot>${project.basedir}/src/main/java9</compileSourceRoot> + </compileSourceRoots> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>default-jar</id> + <configuration> + <archive> + <manifestEntries> + <Multi-Release>true</Multi-Release> + </manifestEntries> + </archive> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </pluginManagement> + </build> + </profile> + <profile> <id>run-its</id> <build> <plugins> diff --git a/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkExecutor.java b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkExecutor.java new file mode 100644 index 0000000..16864b6 --- /dev/null +++ b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkExecutor.java @@ -0,0 +1,72 @@ +package org.apache.maven.plugins.jlink; + +/* + * 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 + * + * 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. + */ + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.toolchain.Toolchain; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +abstract class AbstractJLinkExecutor +{ + protected static final String JMODS = "jmods"; + + private final Toolchain toolchain; + private final Log log; + + private final List<String> modulesToAdd = new ArrayList<>(); + private final List<String> modulePaths = new ArrayList<>(); + + AbstractJLinkExecutor( Toolchain toolchain, Log log ) throws IOException + { + this.toolchain = toolchain; + this.log = log; + } + + protected Toolchain getToolchain() + { + return this.toolchain; + } + + protected Log getLog() + { + return this.log; + } + + public abstract Optional<File> getJmodsFolder( /* nullable */ File sourceJdkModules ); + + public abstract int executeJlink( File argsFile ) throws MojoExecutionException; + + public void addAllModules( Collection<String> modulesToAdd ) + { + this.modulesToAdd.addAll( modulesToAdd ); + } + + public void addAllModulePaths( Collection<String> pathsOfModules ) + { + this.modulePaths.addAll( pathsOfModules ); + } +} diff --git a/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkMojo.java b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkMojo.java index 39e32ac..7443b76 100644 --- a/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkMojo.java +++ b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkMojo.java @@ -25,21 +25,14 @@ import java.lang.reflect.Method; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Properties; -import org.apache.commons.lang3.SystemUtils; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.toolchain.Toolchain; import org.apache.maven.toolchain.ToolchainManager; -import org.codehaus.plexus.util.StringUtils; -import org.codehaus.plexus.util.cli.CommandLineException; -import org.codehaus.plexus.util.cli.CommandLineUtils; -import org.codehaus.plexus.util.cli.Commandline; /** * @author Karl Heinz Marbaise <a href="mailto:[email protected]">[email protected]</a> @@ -66,136 +59,10 @@ public abstract class AbstractJLinkMojo @Component private ToolchainManager toolchainManager; - protected String getJLinkExecutable() - throws IOException + protected JLinkExecutor getJlinkExecutor() + throws IOException { - Toolchain tc = getToolchain(); - - String jLinkExecutable = null; - if ( tc != null ) - { - jLinkExecutable = tc.findTool( "jlink" ); - } - - // TODO: Check if there exist a more elegant way? - String jLinkCommand = "jlink" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" ); - - File jLinkExe; - - if ( StringUtils.isNotEmpty( jLinkExecutable ) ) - { - jLinkExe = new File( jLinkExecutable ); - - if ( jLinkExe.isDirectory() ) - { - jLinkExe = new File( jLinkExe, jLinkCommand ); - } - - if ( SystemUtils.IS_OS_WINDOWS && jLinkExe.getName().indexOf( '.' ) < 0 ) - { - jLinkExe = new File( jLinkExe.getPath() + ".exe" ); - } - - if ( !jLinkExe.isFile() ) - { - throw new IOException( "The jlink executable '" + jLinkExe + "' doesn't exist or is not a file." ); - } - return jLinkExe.getAbsolutePath(); - } - - // ---------------------------------------------------------------------- - // Try to find jlink from System.getProperty( "java.home" ) - // By default, System.getProperty( "java.home" ) = JRE_HOME and JRE_HOME - // should be in the JDK_HOME - // ---------------------------------------------------------------------- - jLinkExe = new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "bin", jLinkCommand ); - - // ---------------------------------------------------------------------- - // Try to find javadocExe from JAVA_HOME environment variable - // ---------------------------------------------------------------------- - if ( !jLinkExe.exists() || !jLinkExe.isFile() ) - { - Properties env = CommandLineUtils.getSystemEnvVars(); - String javaHome = env.getProperty( "JAVA_HOME" ); - if ( StringUtils.isEmpty( javaHome ) ) - { - throw new IOException( "The environment variable JAVA_HOME is not correctly set." ); - } - if ( !new File( javaHome ).getCanonicalFile().exists() - || new File( javaHome ).getCanonicalFile().isFile() ) - { - throw new IOException( "The environment variable JAVA_HOME=" + javaHome - + " doesn't exist or is not a valid directory." ); - } - - jLinkExe = new File( javaHome + File.separator + "bin", jLinkCommand ); - } - - if ( !jLinkExe.getCanonicalFile().exists() || !jLinkExe.getCanonicalFile().isFile() ) - { - throw new IOException( "The jlink executable '" + jLinkExe - + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable." ); - } - - return jLinkExe.getAbsolutePath(); - } - - protected void executeCommand( Commandline cmd, File outputDirectory ) - throws MojoExecutionException - { - if ( getLog().isDebugEnabled() ) - { - // no quoted arguments ??? - getLog().debug( CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) ); - } - - CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer(); - CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer(); - try - { - int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err ); - - String output = ( StringUtils.isEmpty( out.getOutput() ) ? null : '\n' + out.getOutput().trim() ); - - if ( exitCode != 0 ) - { - - if ( StringUtils.isNotEmpty( output ) ) - { - // Reconsider to use WARN / ERROR ? - // getLog().error( output ); - for ( String outputLine : output.split( "\n" ) ) - { - getLog().error( outputLine ); - } - } - - StringBuilder msg = new StringBuilder( "\nExit code: " ); - msg.append( exitCode ); - if ( StringUtils.isNotEmpty( err.getOutput() ) ) - { - msg.append( " - " ).append( err.getOutput() ); - } - msg.append( '\n' ); - msg.append( "Command line was: " ).append( cmd ).append( '\n' ).append( '\n' ); - - throw new MojoExecutionException( msg.toString() ); - } - - if ( StringUtils.isNotEmpty( output ) ) - { - //getLog().info( output ); - for ( String outputLine : output.split( "\n" ) ) - { - getLog().info( outputLine ); - } - } - } - catch ( CommandLineException e ) - { - throw new MojoExecutionException( "Unable to execute jlink command: " + e.getMessage(), e ); - } - + return new JLinkExecutor( getToolchain(), getLog() ); } protected Toolchain getToolchain() @@ -207,12 +74,12 @@ public abstract class AbstractJLinkMojo // Maven 3.3.1 has plugin execution scoped Toolchain Support try { - Method getToolchainsMethod = toolchainManager.getClass().getMethod( "getToolchains", MavenSession.class, - String.class, Map.class ); + Method getToolchainsMethod = toolchainManager.getClass().getMethod( "getToolchains", + MavenSession.class, String.class, Map.class ); @SuppressWarnings( "unchecked" ) - List<Toolchain> tcs = - (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, session, "jdk", jdkToolchain ); + List<Toolchain> tcs = (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, getSession(), + "jdk", jdkToolchain ); if ( tcs != null && tcs.size() > 0 ) { @@ -236,7 +103,7 @@ public abstract class AbstractJLinkMojo if ( tc == null ) { // TODO: Check if we should make the type configurable? - tc = toolchainManager.getToolchainFromBuildContext( "jdk", session ); + tc = toolchainManager.getToolchainFromBuildContext( "jdk", getSession() ); } return tc; diff --git a/src/main/java/org/apache/maven/plugins/jlink/JLinkExecutor.java b/src/main/java/org/apache/maven/plugins/jlink/JLinkExecutor.java new file mode 100644 index 0000000..edd0746 --- /dev/null +++ b/src/main/java/org/apache/maven/plugins/jlink/JLinkExecutor.java @@ -0,0 +1,171 @@ +package org.apache.maven.plugins.jlink; + +/* + * 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 + * + * 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. + */ + +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.toolchain.Toolchain; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.cli.CommandLineUtils; +import org.codehaus.plexus.util.cli.Commandline; + +import java.io.File; +import java.io.IOException; +import java.util.Optional; +import java.util.Properties; + +/** + * JDK 8-only Jlink executor. + * + * <p>As JDK8 does not ship jlink, either a toolchain or JAVA_HOME is required.</p> + */ +class JLinkExecutor extends AbstractJLinkExecutor +{ + private final String jLinkExec; + + JLinkExecutor( Toolchain toolchain, Log log ) throws IOException + { + super( toolchain, log ); + this.jLinkExec = getJLinkExecutable(); + } + + public File getJlinkExecutable() + { + return new File( this.jLinkExec ); + } + + @Override + public Optional<File> getJmodsFolder( /* nullable */ File sourceJdkModules ) + { + // Really Hacky...do we have a better solution to find the jmods directory of the JDK? + File jLinkParent = getJlinkExecutable().getParentFile().getParentFile(); + File jmodsFolder; + if ( sourceJdkModules != null && sourceJdkModules.isDirectory() ) + { + jmodsFolder = new File( sourceJdkModules, JMODS ); + } + else + { + jmodsFolder = new File( jLinkParent, JMODS ); + } + + getLog().debug( " Parent: " + jLinkParent.getAbsolutePath() ); + getLog().debug( " jmodsFolder: " + jmodsFolder.getAbsolutePath() ); + + return Optional.of( jmodsFolder ); + } + + /** + * Execute JLink via any means. + * + * @return the exit code ({@code 0} on success). + */ + @Override + public int executeJlink( File argsFile ) + { + getLog().info( "Toolchain in maven-jlink-plugin: jlink [ " + this.jLinkExec + " ]" ); + + Commandline cmd = createJLinkCommandLine( argsFile ); + cmd.setExecutable( this.jLinkExec ); + + throw new UnsupportedOperationException( "not implemented" ); + } + + private Commandline createJLinkCommandLine( File argsFile ) + { + Commandline cmd = new Commandline(); + cmd.createArg().setValue( '@' + argsFile.getAbsolutePath() ); + + return cmd; + } + + + protected final String getJLinkExecutable() throws IOException + { + String jLinkExecutable = null; + if ( getToolchain() != null ) + { + jLinkExecutable = getToolchain().findTool( "jlink" ); + } + + // TODO: Check if there exist a more elegant way? + String jLinkCommand = "jlink" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" ); + + File jLinkExe; + + if ( StringUtils.isNotEmpty( jLinkExecutable ) ) + { + jLinkExe = new File( jLinkExecutable ); + + if ( jLinkExe.isDirectory() ) + { + jLinkExe = new File( jLinkExe, jLinkCommand ); + } + + if ( SystemUtils.IS_OS_WINDOWS && jLinkExe.getName().indexOf( '.' ) < 0 ) + { + jLinkExe = new File( jLinkExe.getPath() + ".exe" ); + } + + if ( !jLinkExe.isFile() ) + { + throw new IOException( "The jlink executable '" + jLinkExe + "' doesn't exist or is not a file." ); + } + return jLinkExe.getAbsolutePath(); + } + + // ---------------------------------------------------------------------- + // Try to find jlink from System.getProperty( "java.home" ) + // By default, System.getProperty( "java.home" ) = JRE_HOME and JRE_HOME + // should be in the JDK_HOME + // ---------------------------------------------------------------------- + jLinkExe = new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "bin", jLinkCommand ); + + // ---------------------------------------------------------------------- + // Try to find javadocExe from JAVA_HOME environment variable + // ---------------------------------------------------------------------- + if ( !jLinkExe.exists() || !jLinkExe.isFile() ) + { + Properties env = CommandLineUtils.getSystemEnvVars(); + String javaHome = env.getProperty( "JAVA_HOME" ); + if ( StringUtils.isEmpty( javaHome ) ) + { + throw new IOException( "The environment variable JAVA_HOME is not correctly set." ); + } + if ( !new File( javaHome ).getCanonicalFile().exists() || new File( javaHome ).getCanonicalFile().isFile() ) + { + throw new IOException( + "The environment variable JAVA_HOME=" + javaHome + + " doesn't exist or is not a valid directory." ); + } + + jLinkExe = new File( javaHome + File.separator + "bin", jLinkCommand ); + } + + if ( !jLinkExe.getCanonicalFile().exists() || !jLinkExe.getCanonicalFile().isFile() ) + { + throw new IOException( + "The jlink executable '" + jLinkExe + + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable." ); + } + + return jLinkExe.getAbsolutePath(); + } +} diff --git a/src/main/java/org/apache/maven/plugins/jlink/JLinkMojo.java b/src/main/java/org/apache/maven/plugins/jlink/JLinkMojo.java index 14bebdc..790859e 100644 --- a/src/main/java/org/apache/maven/plugins/jlink/JLinkMojo.java +++ b/src/main/java/org/apache/maven/plugins/jlink/JLinkMojo.java @@ -49,7 +49,6 @@ import org.codehaus.plexus.languages.java.jpms.LocationManager; import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest; import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult; import org.codehaus.plexus.util.FileUtils; -import org.codehaus.plexus.util.cli.Commandline; /** * The JLink goal is intended to create a Java Run Time Image file based on @@ -64,8 +63,6 @@ import org.codehaus.plexus.util.cli.Commandline; public class JLinkMojo extends AbstractJLinkMojo { - private static final String JMODS = "jmods"; - @Component private LocationManager locationManager; @@ -274,41 +271,20 @@ public class JLinkMojo @Parameter( defaultValue = "${project.build.finalName}", readonly = true ) private String finalName; - public void execute() - throws MojoExecutionException, MojoFailureException + @Override + public void execute() throws MojoExecutionException, MojoFailureException { - - String jLinkExec = getExecutable(); - - getLog().info( "Toolchain in maven-jlink-plugin: jlink [ " + jLinkExec + " ]" ); - - // TODO: Find a more better and cleaner way? - File jLinkExecuteable = new File( jLinkExec ); - - // Really Hacky...do we have a better solution to find the jmods directory of the JDK? - File jLinkParent = jLinkExecuteable.getParentFile().getParentFile(); - File jmodsFolder; - if ( sourceJdkModules != null && sourceJdkModules.isDirectory() ) - { - jmodsFolder = new File ( sourceJdkModules, JMODS ); - } - else - { - jmodsFolder = new File( jLinkParent, JMODS ); - } - - getLog().debug( " Parent: " + jLinkParent.getAbsolutePath() ); - getLog().debug( " jmodsFolder: " + jmodsFolder.getAbsolutePath() ); - failIfParametersAreNotInTheirValidValueRanges(); ifOutputDirectoryExistsDelteIt(); + JLinkExecutor jLinkExec = getExecutor(); Collection<String> modulesToAdd = new ArrayList<>(); if ( addModules != null ) { modulesToAdd.addAll( addModules ); } + jLinkExec.addAllModules( modulesToAdd ); Collection<String> pathsOfModules = new ArrayList<>(); if ( modulePaths != null ) @@ -326,27 +302,29 @@ public class JLinkMojo } // The jmods directory of the JDK - pathsOfModules.add( jmodsFolder.getAbsolutePath() ); + jLinkExec.getJmodsFolder( this.sourceJdkModules ).ifPresent( + jmodsFolder -> pathsOfModules.add( jmodsFolder.getAbsolutePath() ) + ); + jLinkExec.addAllModulePaths( pathsOfModules ); - Commandline cmd; + File argsFile; try { - cmd = createJLinkCommandLine( pathsOfModules, modulesToAdd ); + argsFile = createJlinkArgsFile( pathsOfModules, modulesToAdd ); } catch ( IOException e ) { throw new MojoExecutionException( e.getMessage() ); } - cmd.setExecutable( jLinkExec ); - executeCommand( cmd, outputDirectoryImage ); + jLinkExec.executeJlink( argsFile ); File createZipArchiveFromImage = createZipArchiveFromImage( buildDirectory, outputDirectoryImage ); if ( projectHasAlreadySetAnArtifact() ) { throw new MojoExecutionException( "You have to use a classifier " - + "to attach supplemental artifacts to the project instead of replacing them." ); + + "to attach supplemental artifacts to the project instead of replacing them." ); } getProject().getArtifact().setFile( createZipArchiveFromImage ); @@ -443,13 +421,12 @@ public class JLinkMojo return modulepathElements; } - private String getExecutable() - throws MojoFailureException + private JLinkExecutor getExecutor() throws MojoFailureException { - String jLinkExec; + JLinkExecutor jLinkExec; try { - jLinkExec = getJLinkExecutable(); + jLinkExec = getJlinkExecutor(); } catch ( IOException e ) { @@ -510,7 +487,7 @@ public class JLinkMojo if ( endian != null && ( !"big".equals( endian ) && !"little".equals( endian ) ) ) { String message = "The given endian parameter " + endian - + " does not contain one of the following values: 'little' or 'big'."; + + " does not contain one of the following values: 'little' or 'big'."; getLog().error( message ); throw new MojoFailureException( message ); } @@ -537,10 +514,10 @@ public class JLinkMojo } } - private Commandline createJLinkCommandLine( Collection<String> pathsOfModules, Collection<String> modulesToAdd ) - throws IOException + private File createJlinkArgsFile( Collection<String> pathsOfModules, + Collection<String> modulesToAdd ) throws IOException { - File file = new File( outputDirectoryImage.getParentFile(), "jlinkArgs" ); + File file = new File( this.outputDirectoryImage.getParentFile(), "jlinkArgs" ); if ( !getLog().isDebugEnabled() ) { file.deleteOnExit(); @@ -656,11 +633,7 @@ public class JLinkMojo argsFile.println( "--verbose" ); } argsFile.close(); - - Commandline cmd = new Commandline(); - cmd.createArg().setValue( '@' + file.getAbsolutePath() ); - - return cmd; + return file; } private boolean hasIncludeLocales() diff --git a/src/main/java9/org/apache/maven/plugins/jlink/JLinkExecutor.java b/src/main/java9/org/apache/maven/plugins/jlink/JLinkExecutor.java new file mode 100644 index 0000000..02c4333 --- /dev/null +++ b/src/main/java9/org/apache/maven/plugins/jlink/JLinkExecutor.java @@ -0,0 +1,175 @@ +/* + * 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 + * + * 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 org.apache.maven.plugins.jlink; + +import org.apache.commons.lang3.StringUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.toolchain.Toolchain; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class JLinkExecutor extends AbstractJLinkExecutor +{ + + private final ToolProvider toolProvider; + + JLinkExecutor( Toolchain toolchain, Log log ) throws IOException + { + super( toolchain, log ); + this.toolProvider = getJLinkExecutable(); + } + + @Override + public Optional<File> getJmodsFolder( /* nullable */ File sourceJdkModules ) + { + if ( sourceJdkModules != null && sourceJdkModules.isDirectory() ) + { + return Optional.of( new File( sourceJdkModules, JMODS ) ); + } + + // ToolProvider does not need jmods folder to be set. + return Optional.empty(); + } + + protected final ToolProvider getJLinkExecutable() + { + Optional<ToolProvider> jlink = ToolProvider.findFirst( "jlink" ); + + if ( !jlink.isPresent() ) + { + throw new IllegalStateException( "No jlink tool found." ); + } + + return jlink.orElseThrow( NoSuchElementException::new ); + } + + + protected Stream<String> argsfileToArgs( File argsFile ) + { + try + { + List<String> strings = Files.readAllLines( argsFile.toPath() ); + Deque<String> out = new ArrayDeque<>(); + + for ( String line : strings ) + { + if ( line.startsWith( "-" ) ) + { + out.add( line ); + continue; + } + + if ( line.startsWith( "\"" ) && line.endsWith( "\"" ) ) + { + out.add( line.substring( 1, line.lastIndexOf( "\"" ) ) ); + continue; + } + + out.add( line ); + } + + return out.stream(); + } + catch ( IOException e ) + { + throw new IllegalStateException( "Unable to read jlinkArgs file: " + argsFile.getAbsolutePath() ); + } + + } + + @Override + public int executeJlink( File argsFile ) throws MojoExecutionException + { + List<String> actualArgs = this.argsfileToArgs( argsFile ).collect( Collectors.toList() ); + + if ( getLog().isDebugEnabled() ) + { + // no quoted arguments ??? + getLog().debug( this.toolProvider.name() + " " + actualArgs ); + } + + try ( ByteArrayOutputStream baosErr = new ByteArrayOutputStream(); + PrintWriter err = new PrintWriter( baosErr ); + ByteArrayOutputStream baosOut = new ByteArrayOutputStream(); + PrintWriter out = new PrintWriter( baosOut ) ) + { + int exitCode = this.toolProvider.run( out, err, actualArgs.toArray( new String[0] ) ); + out.flush(); + err.flush(); + + String outAsString = baosOut.toString( "UTF-8" ); + String output = ( StringUtils.isEmpty( outAsString ) ? null : '\n' + outAsString.trim() ); + + if ( exitCode != 0 ) + { + if ( StringUtils.isNotEmpty( output ) ) + { + // Reconsider to use WARN / ERROR ? + // getLog().error( output ); + for ( String outputLine : output.split( "\n" ) ) + { + getLog().error( outputLine ); + } + } + + StringBuilder msg = new StringBuilder( "\nExit code: " ); + msg.append( exitCode ); + String errAsString = baosErr.toString(); + if ( StringUtils.isNotEmpty( errAsString ) ) + { + msg.append( " - " ).append( errAsString ); + } + msg.append( '\n' ); + msg.append( "Command line was: " ).append( this.toolProvider.name() ).append( ' ' ).append( + actualArgs ).append( '\n' ).append( '\n' ); + + throw new MojoExecutionException( msg.toString() ); + } + + if ( StringUtils.isNotEmpty( output ) ) + { + //getLog().info( output ); + for ( String outputLine : output.split( "\n" ) ) + { + getLog().info( outputLine ); + } + } + + return exitCode; + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Unable to execute jlink command: " + e.getMessage(), e ); + } + } +}
