View Javadoc
1   package org.apache.maven.plugins.jmod;
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 java.io.File;
23  import java.io.IOException;
24  import java.io.PrintStream;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.LinkedHashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Map.Entry;
32  
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugin.MojoFailureException;
36  import org.apache.maven.plugins.annotations.Component;
37  import org.apache.maven.plugins.annotations.LifecyclePhase;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.plugins.annotations.ResolutionScope;
41  import org.apache.maven.project.MavenProject;
42  import org.apache.maven.shared.utils.StringUtils;
43  import org.apache.maven.shared.utils.logging.MessageUtils;
44  import org.apache.maven.toolchain.Toolchain;
45  import org.apache.maven.toolchain.java.DefaultJavaToolChain;
46  import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
47  import org.codehaus.plexus.languages.java.jpms.LocationManager;
48  import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
49  import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
50  import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult.ModuleNameSource;
51  import org.codehaus.plexus.util.FileUtils;
52  import org.codehaus.plexus.util.cli.Commandline;
53  
54  /**
55   * The <code>create</code> goal is intended to create <code>jmod</code> files which can be used for later linking via
56   * <a href="https://maven.apache.org/plugins/maven-jlink-plugin/">maven-jlink-plugin</a>. The <code>jmod</code> files
57   * can not be used as usual dependencies on the classpath only in relationship with <code>maven-jlink-plugin</code>.
58   * 
59   * @author Karl Heinz Marbaise <a href="mailto:khmarbaise@apache.org">khmarbaise@apache.org</a>
60   */
61  // CHECKSTYLE_OFF: LineLength
62  @Mojo( name = "create", requiresDependencyResolution = ResolutionScope.COMPILE, defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true )
63  // CHECKSTYLE_ON: LineLength
64  public class JModCreateMojo
65      extends AbstractJModMojo
66  {
67      private static final String JMODS = "jmods";
68  
69      private List<String> classpathElements;
70  
71      private List<String> modulepathElements;
72  
73      private Map<String, JavaModuleDescriptor> pathElements;
74  
75      @Parameter( defaultValue = "${project.compileClasspathElements}", readonly = true, required = true )
76      private List<String> compilePath;
77  
78      @Component
79      private LocationManager locationManager;
80  
81      /**
82       * Specifies one or more directories containing native commands to be copied. The given directories are relative to
83       * the current base directory. If no entry is defined the default is <code>src/main/cmds</code> used.
84       * 
85       * <pre>
86       * &lt;cmds&gt;
87       *   &lt;cmd&gt;...&lt;/cmd&gt;
88       *   &lt;cmd&gt;...&lt;/cmd&gt;
89       *   .
90       *   .
91       * &lt;/cmds&gt;
92       * </pre>
93       * <p>
94       * All files from those directories will be copied into the resulting directory <code>bin</code> within the jmod
95       * file.
96       * </p>
97       * <code>JMod</code> command line equivalent: <code>--cmds &lt;path&gt;</code>.
98       */
99      @Parameter
100     private List<String> cmds;
101 
102     private static final String DEFAULT_CMD_DIRECTORY = "src/main/cmds";
103 
104     /**
105      * Specifies one or more directories containing configuration files to be copied. Location of user-editable config
106      * files. If no configuration is given the <code>src/main/configs</code> location is used as default. If this
107      * directory does not exist the whole will be ignored.
108      * 
109      * <pre>
110      * &lt;configs&gt;
111      *   &lt;config&gt;...&lt;/config&gt;
112      *   &lt;config&gt;...&lt;/config&gt;
113      *   .
114      *   .
115      * &lt;/configs&gt;
116      * </pre>
117      * <p>
118      * All files from those directories will be copied into the resulting directory <code>config</code> within the jmod
119      * file.
120      * </p>
121      * jmod command line equivalent: <code>--config &lt;path&gt;</code>.
122      */
123     @Parameter
124     private List<String> configs;
125 
126     private static final String DEFAULT_CONFIG_DIRECTORY = "src/main/configs";
127 
128     /**
129      * Exclude files matching the pattern list. Each element using one the following forms: &lt;glob-pattern&gt;,
130      * glob:&lt;glob-pattern&gt; or regex:&lt;regex-pattern&gt;
131      * 
132      * <pre>
133      * &lt;excludes&gt;
134      *   &lt;exclude&gt;...&lt;/exclude&gt;
135      *   &lt;exclude&gt;...&lt;/exclude&gt;
136      *   .
137      *   .
138      * &lt;/excludes&gt;
139      * </pre>
140      */
141     @Parameter
142     private List<String> excludes;
143 
144     /**
145      * Define the main class which is recorded in the <code>module-info.class</code> file.
146      */
147     @Parameter
148     private String mainClass;
149 
150     /**
151      * Specifies one or more directories containing native libraries to be copied (The given directories are relative to
152      * project base directory). If no configuration is given in <<pom.xml>> file the location <code>src/main/libs</code>
153      * will be used. If the default location does not exist the whole configuration will be ignored.
154      * 
155      * <pre>
156      * &lt;libs&gt;
157      *   &lt;lib&gt;...&lt;/lib&gt;
158      *   &lt;lib&gt;...&lt;/lib&gt;
159      *   .
160      *   .
161      * &lt;/libs&gt;
162      * </pre>
163      * <p>
164      * All files from those directories will be copied into the resulting directory <code>lib</code> within the jmod
165      * file.
166      * </p>
167      */
168     @Parameter
169     private List<String> libs;
170 
171     private static final String DEFAULT_LIB_DIRECTORY = "src/main/libs";
172 
173     /**
174      * Define the module version of the jmod file.
175      */
176     @Parameter( defaultValue = "${project.version}" )
177     private String moduleVersion;
178 
179     /**
180      * <code>--do-not-resolve-by-default</code> Exclude from the default root set of modules
181      */
182     @Parameter( defaultValue = "false" )
183     private boolean doNotResolveByDefault;
184 
185     /**
186      * Define the locations of header files. The default location is <code>src/main/headerfiles</code>. If the the
187      * default location does not exist in the current project it will be ignored. The given directories are relative to
188      * the project base directory. If an entry is defined the definition of all locations is needed.
189      * 
190      * <pre>
191      * &lt;headerFiles&gt;
192      *   &lt;headerFile&gt;...&lt;/headerFile&gt;
193      *   &lt;headerFile&gt;...&lt;/headerFile&gt;
194      *   .
195      *   .
196      * &lt;/headerFiles&gt;
197      * </pre>
198      * <p>
199      * All files from those directories will be copied into the resulting directory <code>includes</code> within the
200      * jmod file.
201      * </p>
202      * jmod command line equivalent <code>--header-files &lt;path&gt;</code>
203      */
204     @Parameter
205     private List<String> headerFiles;
206 
207     private static final String DEFAULT_HEADER_FILES_DIRECTORY = "src/main/headerfiles";
208 
209     /**
210      * Define the locations of man pages. The default location is <code>src/main/manpages</code>. The given man pages
211      * locations are relative to the project base directory.
212      * 
213      * <pre>
214      * &lt;manPages&gt;
215      *   &lt;manPage&gt;...&lt;/manPage&gt;
216      *   &lt;manPage&gt;...&lt;/manPage&gt;
217      *   .
218      *   .
219      * &lt;/manPages&gt;
220      * </pre>
221      * <p>
222      * All files from those directories will be copied into the resulting directory <code>man</code> within the jmod
223      * file.
224      * </p>
225      * jmod command line equivalent <code>--man-pages &lt;path&gt;</code>
226      */
227     @Parameter
228     private List<String> manPages;
229 
230     private static final String DEFAULT_MAN_PAGES_DIRECTORY = "src/main/manpages";
231 
232     /**
233      * This is only the name of the jmod file in the target directory.
234      */
235     @Parameter( defaultValue = "${project.artifactId}", required = true, readonly = true )
236     private String outputFileName;
237 
238     /**
239      * Define the location of legal notices. The default location is <code>src/main/legalnotices</code>. The given man
240      * pages locations are relative to the project base directory.
241      * 
242      * <pre>
243      * &lt;legalNotices&gt;
244      *   &lt;legalNotice&gt;...&lt;/legalNotice&gt;
245      *   &lt;legalNotice&gt;...&lt;/legalNotice&gt;
246      *   .
247      *   .
248      * &lt;/legalNotices&gt;
249      * </pre>
250      * <p>
251      * All files from those directories will be copied into the resulting directory <code>legal</code> within the jmod
252      * file.
253      * </p>
254      * jmod command line equivalent <code>--legal-notices &lt;path&gt;</code>
255      */
256     @Parameter
257     private List<String> legalNotices;
258 
259     private static final String DEFAULT_LEGAL_NOTICES_DIRECTORY = "src/main/legalnotices";
260 
261     /**
262      * <code>--target-platform &lt;target-platform&gt;</code> Target platform TODO: Which values are valid?
263      */
264     @Parameter
265     private String targetPlatform;
266 
267     /**
268      * Hint for a tool to issue a warning if the module is resolved. The valid values are:
269      * <ul>
270      * <li>deprecated</li>
271      * <li>deprecated-for-removal</li>
272      * <li>incubating</li>
273      * </ul>
274      */
275     @Parameter
276     private String warnIfResolved;
277 
278     @Parameter( defaultValue = "${project.build.outputDirectory}", required = true, readonly = true )
279     private File targetClassesDirectory;
280 
281     @Parameter( defaultValue = "${project.build.directory}", required = true, readonly = true )
282     private File outputDirectory;
283 
284     private List<String> modulePaths;
285 
286     public void execute()
287         throws MojoExecutionException, MojoFailureException
288     {
289 
290         String jModExecutable;
291         try
292         {
293             jModExecutable = getJModExecutable();
294         }
295         catch ( IOException e )
296         {
297             throw new MojoFailureException( "Unable to find jmod command: " + e.getMessage(), e );
298         }
299 
300         File jModExecuteableFile = new File( jModExecutable );
301         File jModExecutableParent = jModExecuteableFile.getParentFile().getParentFile();
302         File jmodsFolderJDK = new File( jModExecutableParent, JMODS );
303         getLog().debug( "Parent: " + jModExecutableParent.getAbsolutePath() );
304         getLog().debug( "jmodsFolder: " + jmodsFolderJDK.getAbsolutePath() );
305 
306         preparePaths();
307 
308         failIfParametersAreNotInTheirValidValueRanges();
309 
310         getLog().info( "Toolchain in maven-jmod-plugin: jmod [ " + jModExecutable + " ]" );
311 
312         // We need to put the resulting x.jmod files into jmods folder otherwise is
313         // seemed to be not working.
314         // Check why?
315         File modsFolder = new File( outputDirectory, "jmods" );
316         File resultingJModFile = new File( modsFolder, outputFileName + ".jmod" );
317 
318         deleteOutputIfAlreadyExists( resultingJModFile );
319 
320         // create the jmods folder...
321         modsFolder.mkdirs();
322 
323         this.modulePaths = new ArrayList<>();
324         for ( Entry<String, JavaModuleDescriptor> item : pathElements.entrySet() )
325         {
326             // Isn't there a better solution?
327             if ( item.getValue() == null )
328             {
329                 String message = "The given dependency " + item.getKey()
330                     + " does not have a module-info.java file. So it can't be linked.";
331                 getLog().error( message );
332                 throw new MojoFailureException( message );
333             }
334             getLog().debug( "pathElements Item:" + item.getKey() + " v:" + item.getValue().name() );
335             getLog().info( " -> module: " + item.getValue().name() + " ( " + item.getKey() + " )" );
336             // We use the real module name and not the artifact Id...
337             this.modulePaths.add( item.getKey() );
338         }
339         // The jmods directory of the JDK
340         this.modulePaths.add( jmodsFolderJDK.getAbsolutePath() );
341 
342         Commandline cmd;
343         try
344         {
345             cmd = createJModCreateCommandLine( resultingJModFile );
346         }
347         catch ( IOException e )
348         {
349             throw new MojoExecutionException( e.getMessage() );
350         }
351         cmd.setExecutable( jModExecutable );
352 
353         executeCommand( cmd, outputDirectory );
354 
355         if ( projectHasAlreadySetAnArtifact() )
356         {
357             throw new MojoExecutionException( "You have to use a classifier "
358                 + "to attach supplemental artifacts to the project instead of replacing them." );
359         }
360 
361         getProject().getArtifact().setFile( resultingJModFile );
362     }
363 
364     private void deleteOutputIfAlreadyExists( File resultingJModFile )
365         throws MojoFailureException
366     {
367         if ( resultingJModFile.exists() && resultingJModFile.isFile() )
368         {
369             try
370             {
371                 getLog().debug( "Deleting the existing " + resultingJModFile.getAbsolutePath() + " file." );
372                 FileUtils.forceDelete( resultingJModFile );
373             }
374             catch ( IOException e )
375             {
376                 String message = "Failure during deleting of file " + resultingJModFile.getAbsolutePath();
377                 getLog().error( message );
378                 throw new MojoFailureException( message );
379             }
380         }
381     }
382 
383     private void failIfParametersAreNotInTheirValidValueRanges()
384         throws MojoFailureException
385     {
386         if ( warnIfResolved != null )
387         {
388             String x = warnIfResolved.toLowerCase().trim();
389             if ( !"deprecated".equals( x ) && "deprecated-for-removal".equals( x ) && "incubating".equals( x ) )
390             {
391                 String message = "The parameter warnIfResolved does not contain a valid value. "
392                     + "Valid values are 'deprecated', 'deprecated-for-removal' or 'incubating'.";
393                 getLog().error( message );
394                 throw new MojoFailureException( message );
395 
396             }
397         }
398 
399         throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( cmds, DEFAULT_CMD_DIRECTORY ),
400                                                  "cmd" );
401         throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( configs,
402                                                                                      DEFAULT_CONFIG_DIRECTORY ),
403                                                  "config" );
404         throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( libs, DEFAULT_LIB_DIRECTORY ),
405                                                  "lib" );
406         throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( headerFiles,
407                                                                                      DEFAULT_HEADER_FILES_DIRECTORY ),
408                                                  "headerFile" );
409         throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( legalNotices,
410                                                                                      DEFAULT_LEGAL_NOTICES_DIRECTORY ),
411                                                  "legalNotice" );
412         throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( manPages,
413                                                                                      DEFAULT_MAN_PAGES_DIRECTORY ),
414                                                  "manPage" );
415     }
416 
417     private void throwExceptionIfNotExistOrNotADirectory( List<String> configurations, String partialMessage )
418         throws MojoFailureException
419     {
420         for ( String configLocation : configurations )
421         {
422             File dir = new File( configLocation );
423             if ( !dir.exists() || !dir.isDirectory() )
424             {
425                 String message = "The directory " + configLocation + " for " + partialMessage
426                     + " parameter does not exist " + "or is not a directory. ";
427                 getLog().error( message );
428                 throw new MojoFailureException( message );
429             }
430         }
431     }
432 
433     private List<File> getCompileClasspathElements( MavenProject project )
434     {
435         List<File> list = new ArrayList<File>( project.getArtifacts().size() + 1 );
436 
437         list.add( new File( project.getBuild().getOutputDirectory() ) );
438 
439         for ( Artifact a : project.getArtifacts() )
440         {
441             list.add( a.getFile() );
442         }
443         return list;
444     }
445 
446     private void preparePaths()
447     {
448         assert compilePath != null;
449 
450         boolean hasModuleDescriptor = false;
451         // Assuming that the module-info.java is already compiled by compiler plugin so only
452         // check if the module-info.class file exists.
453         File moduleInfo = new File( targetClassesDirectory, "module-info.class" );
454 
455         if ( moduleInfo.exists() && moduleInfo.isFile() )
456         {
457             getLog().debug( "We have found a module-info.class file." );
458             hasModuleDescriptor = true;
459         }
460 
461         if ( hasModuleDescriptor )
462         {
463             // For now only allow named modules. Once we can create a graph with ASM we can specify exactly the modules
464             // and we can detect if auto modules are used. In that case, MavenProject.setFile() should not be used, so
465             // you cannot depend on this project and so it won't be distributed.
466 
467             modulepathElements = new ArrayList<String>();
468             classpathElements = new ArrayList<String>();
469             pathElements = new LinkedHashMap<String, JavaModuleDescriptor>();
470 
471             ResolvePathsResult<File> resolvePathsResult;
472             try
473             {
474                 Collection<File> dependencyArtifacts = getCompileClasspathElements( getProject() );
475 
476                 ResolvePathsRequest<File> request = ResolvePathsRequest.withFiles( dependencyArtifacts );
477 
478                 Toolchain toolchain = getToolchain();
479                 if ( toolchain != null && toolchain instanceof DefaultJavaToolChain )
480                 {
481                     request.setJdkHome( new File( ( (DefaultJavaToolChain) toolchain ).getJavaHome() ) );
482                 }
483 
484                 resolvePathsResult = locationManager.resolvePaths( request );
485 
486                 JavaModuleDescriptor moduleDescriptor = resolvePathsResult.getMainModuleDescriptor();
487 
488                 for ( Map.Entry<File, ModuleNameSource> entry : resolvePathsResult.getModulepathElements().entrySet() )
489                 {
490                     getLog().debug( "File: " + entry.getKey().getAbsolutePath() + " " + entry.getValue().name() );
491                     if ( ModuleNameSource.FILENAME.equals( entry.getValue() ) )
492                     {
493                         final String message = "Required filename-based automodules detected. "
494                             + "Please don't publish this project to a public artifact repository!";
495 
496                         if ( moduleDescriptor.exports().isEmpty() )
497                         {
498                             // application
499                             getLog().info( message );
500                         }
501                         else
502                         {
503                             // library
504                             writeBoxedWarning( message );
505                         }
506                         break;
507                     }
508                 }
509 
510                 for ( Map.Entry<File, JavaModuleDescriptor> entry : resolvePathsResult.getPathElements().entrySet() )
511                 {
512                     getLog().debug( "pathElements: " + entry.getKey().getPath() + " " + entry.getValue().name() );
513                     pathElements.put( entry.getKey().getPath(), entry.getValue() );
514                 }
515 
516                 for ( File file : resolvePathsResult.getClasspathElements() )
517                 {
518                     getLog().debug( "classpathElements: File: " + file.getPath() );
519                     classpathElements.add( file.getPath() );
520                 }
521 
522                 for ( File file : resolvePathsResult.getModulepathElements().keySet() )
523                 {
524                     getLog().debug( "modulepathElements: File: " + file.getPath() );
525                     modulepathElements.add( file.getPath() );
526                 }
527             }
528             catch ( IOException e )
529             {
530                 getLog().warn( e.getMessage() );
531             }
532 
533         }
534         else
535         {
536             classpathElements = compilePath;
537             modulepathElements = Collections.emptyList();
538         }
539     }
540 
541     private Commandline createJModCreateCommandLine( File resultingJModFile )
542         throws IOException
543     {
544         File file = new File( outputDirectory, "jmodCreateArgs" );
545         if ( !getLog().isDebugEnabled() )
546         {
547             file.deleteOnExit();
548         }
549         file.getParentFile().mkdirs();
550         file.createNewFile();
551 
552         PrintStream argsFile = new PrintStream( file );
553 
554         argsFile.println( "create" );
555 
556         if ( moduleVersion != null )
557         {
558             argsFile.println( "--module-version" );
559             argsFile.println( moduleVersion );
560         }
561 
562         if ( !pathElements.isEmpty() )
563         {
564             argsFile.println( "--class-path" );
565             //TODO: Can't this be achieved in a more elegant way?
566             // the classpathElements do not contain the needed information?
567             ArrayList<String> x = new ArrayList<>();
568             for ( String string : pathElements.keySet() )
569             {
570                 x.add( string );
571             }
572             argsFile.println( getPlatformSeparatedList( x ) );
573         }
574 
575         if ( excludes != null && !excludes.isEmpty() )
576         {
577             argsFile.println( "--exclude" );
578             String commaSeparatedList = getCommaSeparatedList( excludes );
579             argsFile.append( '"' ).append( commaSeparatedList.replace( "\\", "\\\\" ) ).println( '"' );
580         }
581 
582         List<String> configList = handleConfigurationListWithDefault( configs, DEFAULT_CONFIG_DIRECTORY );
583         if ( !configList.isEmpty() )
584         {
585             argsFile.println( "--config" );
586             // Should we quote the paths?
587             argsFile.println( getPlatformSeparatedList( configList ) );
588         }
589 
590         List<String> cmdsList = handleConfigurationListWithDefault( cmds, DEFAULT_CMD_DIRECTORY );
591         if ( !cmdsList.isEmpty() )
592         {
593             argsFile.println( "--cmds" );
594             argsFile.println( getPlatformSeparatedList( cmdsList ) );
595         }
596 
597         List<String> libsList = handleConfigurationListWithDefault( libs, DEFAULT_LIB_DIRECTORY );
598         if ( !libsList.isEmpty() )
599         {
600             argsFile.println( "--libs" );
601             argsFile.println( getPlatformSeparatedList( libsList ) );
602         }
603 
604         List<String> headerFilesList =
605             handleConfigurationListWithDefault( headerFiles, DEFAULT_HEADER_FILES_DIRECTORY );
606         if ( !headerFilesList.isEmpty() )
607         {
608             argsFile.println( "--header-files" );
609             argsFile.println( getPlatformSeparatedList( headerFilesList ) );
610         }
611 
612         List<String> legalNoticesList =
613             handleConfigurationListWithDefault( legalNotices, DEFAULT_LEGAL_NOTICES_DIRECTORY );
614         if ( !legalNoticesList.isEmpty() )
615         {
616             argsFile.println( "--legal-notices" );
617             argsFile.println( getPlatformSeparatedList( legalNoticesList ) );
618         }
619 
620         List<String> manPagesList = handleConfigurationListWithDefault( manPages, DEFAULT_MAN_PAGES_DIRECTORY );
621         if ( !manPagesList.isEmpty() )
622         {
623             argsFile.println( "--man-pages" );
624             argsFile.println( getPlatformSeparatedList( manPagesList ) );
625         }
626 
627         if ( modulePaths != null )
628         {
629             //@formatter:off
630             argsFile.println( "--module-path" );
631             argsFile
632               .append( '"' )
633               .append( getPlatformSeparatedList( modulePaths ).replace( "\\", "\\\\" ) ) 
634               .println( '"' );
635             //@formatter:off
636         }
637 
638         if ( targetPlatform != null )
639         {
640             argsFile.println( "--target-platform" );
641             argsFile.println( targetPlatform );
642         }
643 
644         if ( warnIfResolved != null )
645         {
646             argsFile.println( "--warn-if-resolved" );
647             argsFile.println( warnIfResolved );
648         }
649 
650         if ( doNotResolveByDefault )
651         {
652             argsFile.println( "--do-not-resolve-by-default" );
653         }
654 
655         argsFile.println( resultingJModFile.getAbsolutePath() );
656         argsFile.close();
657 
658         Commandline cmd = new Commandline();
659         cmd.createArg().setValue( '@' + file.getAbsolutePath() );
660 
661         return cmd;
662     }
663 
664     private boolean isConfigurationDefinedInPOM( List<String> configuration )
665     {
666         return configuration != null && !configuration.isEmpty();
667     }
668 
669     private List<String> handleConfigurationListWithDefault( List<String> configuration, String defaultLocation )
670     {
671         List<String> commands = new ArrayList<String>();
672         if ( isConfigurationDefinedInPOM( configuration ) )
673         {
674             commands.addAll( configuration );
675         }
676         else
677         {
678             if ( doDefaultsExist( defaultLocation ) )
679             {
680                 commands.add( defaultLocation );
681             }
682         }
683 
684         commands = resolveAgainstProjectBaseDir( commands );
685         return commands;
686     }
687 
688     private List<String> resolveAgainstProjectBaseDir( List<String> relativeDirectories )
689     {
690         List<String> result = new ArrayList<>();
691 
692         for ( String configLocation : relativeDirectories )
693         {
694             File dir = new File( getProject().getBasedir(), configLocation );
695             result.add( dir.getAbsolutePath() );
696         }
697         return result;
698     }
699 
700     private boolean doDefaultsExist( String defaultLocation )
701     {
702         boolean result = false;
703         File dir = new File( getProject().getBasedir(), defaultLocation );
704         if ( dir.exists() && dir.isDirectory() )
705         {
706             result = true;
707         }
708         return result;
709     }
710 
711     private String getPlatformSeparatedList( List<String> paths )
712     {
713         StringBuilder sb = new StringBuilder();
714         for ( String module : paths )
715         {
716             if ( sb.length() > 0 )
717             {
718                 sb.append( File.pathSeparatorChar );
719             }
720             sb.append( module );
721         }
722         return sb.toString();
723     }
724 
725     private void writeBoxedWarning( String message )
726     {
727         String line = StringUtils.repeat( "*", message.length() + 4 );
728         getLog().warn( line );
729         getLog().warn( "* " + MessageUtils.buffer().strong( message )  + " *" );
730         getLog().warn( line );
731     }
732 
733 }