View Javadoc
1   package org.apache.maven.plugins.jlink;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.lang3.StringUtils;
23  import org.apache.commons.lang3.SystemUtils;
24  import org.apache.maven.plugin.MojoExecutionException;
25  import org.apache.maven.plugin.logging.Log;
26  import org.apache.maven.shared.utils.cli.CommandLineException;
27  import org.apache.maven.shared.utils.cli.CommandLineUtils;
28  import org.apache.maven.shared.utils.cli.Commandline;
29  import org.apache.maven.toolchain.Toolchain;
30  
31  import java.io.File;
32  import java.util.List;
33  import java.util.NoSuchElementException;
34  import java.util.Optional;
35  
36  abstract class AbstractJLinkToolchainExecutor extends AbstractJLinkExecutor
37  {
38      private final Toolchain toolchain;
39  
40      AbstractJLinkToolchainExecutor( Toolchain toolchain, Log log )
41      {
42          super( log );
43          this.toolchain = toolchain;
44      }
45  
46      protected Optional<Toolchain> getToolchain()
47      {
48          return Optional.ofNullable( this.toolchain );
49      }
50  
51      /**
52       * Execute JLink via toolchain.
53       *
54       * @return the exit code ({@code 0} on success).
55       */
56      @Override
57      public int executeJlink( List<String> jlinkArgs ) throws MojoExecutionException
58      {
59          File jlinkExecutable = getJlinkExecutable();
60          getLog().info( "Toolchain in maven-jlink-plugin: jlink [ " + jlinkExecutable + " ]" );
61  
62          Commandline cmd = createJLinkCommandLine( jlinkArgs );
63          cmd.setExecutable( jlinkExecutable.getAbsolutePath() );
64  
65          return executeCommand( cmd );
66      }
67  
68      private File getJlinkExecutable()
69      {
70          return new File( getJLinkExecutable() );
71      }
72  
73      @Override
74      public Optional<File> getJmodsFolder( /* nullable */ File sourceJdkModules )
75      {
76          // Really Hacky...do we have a better solution to find the jmods directory of the JDK?
77          File jLinkParent = getJlinkExecutable().getParentFile().getParentFile();
78          File jmodsFolder;
79          if ( sourceJdkModules != null && sourceJdkModules.isDirectory() )
80          {
81              jmodsFolder = new File( sourceJdkModules, JMODS );
82          }
83          else
84          {
85              jmodsFolder = new File( jLinkParent, JMODS );
86          }
87  
88          getLog().debug( " Parent: " + jLinkParent.getAbsolutePath() );
89          getLog().debug( " jmodsFolder: " + jmodsFolder.getAbsolutePath() );
90  
91          return Optional.of( jmodsFolder );
92      }
93  
94      private Commandline createJLinkCommandLine( List<String> jlinkArgs )
95      {
96          Commandline cmd = new Commandline();
97          jlinkArgs.forEach( arg -> cmd.createArg().setValue( arg ) );
98  
99          return cmd;
100     }
101 
102     private String getJLinkExecutable()
103     {
104         Optional<Toolchain> toolchain = getToolchain();
105 
106         if ( !toolchain.isPresent() )
107         {
108             getLog().error( "Either JDK9+ or a toolchain "
109                     + "pointing to a JDK9+ containing a jlink binary is required." );
110             getLog().info( "See https://maven.apache.org/guides/mini/guide-using-toolchains.html "
111                     + "for mor information." );
112             throw new IllegalStateException( "Running on JDK8 and no toolchain found." );
113         }
114 
115         String jLinkExecutable = toolchain.orElseThrow( NoSuchElementException::new ).findTool( "jlink" );
116 
117         if ( jLinkExecutable.isEmpty() )
118         {
119             throw new IllegalStateException( "The jlink executable '"
120                     + jLinkExecutable + "' doesn't exist or is not a file." );
121         }
122 
123         // TODO: Check if there exist a more elegant way?
124         String jLinkCommand = "jlink" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" );
125 
126         File jLinkExe = new File( jLinkExecutable );
127 
128         if ( jLinkExe.isDirectory() )
129         {
130             jLinkExe = new File( jLinkExe, jLinkCommand );
131         }
132 
133         if ( SystemUtils.IS_OS_WINDOWS && jLinkExe.getName().indexOf( '.' ) < 0 )
134         {
135             jLinkExe = new File( jLinkExe.getPath() + ".exe" );
136         }
137 
138         if ( !jLinkExe.isFile() )
139         {
140             throw new IllegalStateException( "The jlink executable '"
141                     + jLinkExe + "' doesn't exist or is not a file." );
142         }
143         return jLinkExe.getAbsolutePath();
144     }
145 
146     private int executeCommand( Commandline cmd )
147             throws MojoExecutionException
148     {
149         if ( getLog().isDebugEnabled() )
150         {
151             // no quoted arguments ???
152             getLog().debug( CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) );
153         }
154 
155         CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
156         CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
157         try
158         {
159             int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err );
160 
161             String output = ( StringUtils.isEmpty( out.getOutput() ) ? null : '\n' + out.getOutput().trim() );
162 
163             if ( exitCode != 0 )
164             {
165 
166                 if ( StringUtils.isNotEmpty( output ) )
167                 {
168                     // Reconsider to use WARN / ERROR ?
169                     //  getLog().error( output );
170                     for ( String outputLine : output.split( "\n" ) )
171                     {
172                         getLog().error( outputLine );
173                     }
174                 }
175 
176                 StringBuilder msg = new StringBuilder( "\nExit code: " );
177                 msg.append( exitCode );
178                 if ( StringUtils.isNotEmpty( err.getOutput() ) )
179                 {
180                     msg.append( " - " ).append( err.getOutput() );
181                 }
182                 msg.append( '\n' );
183                 msg.append( "Command line was: " ).append( cmd ).append( '\n' ).append( '\n' );
184 
185                 throw new MojoExecutionException( msg.toString() );
186             }
187 
188             if ( StringUtils.isNotEmpty( output ) )
189             {
190                 //getLog().info( output );
191                 for ( String outputLine : output.split( "\n" ) )
192                 {
193                     getLog().info( outputLine );
194                 }
195             }
196 
197             return exitCode;
198         }
199         catch ( CommandLineException e )
200         {
201             throw new MojoExecutionException( "Unable to execute jlink command: " + e.getMessage(), e );
202         }
203     }
204 }