View Javadoc
1   package org.apache.maven.shared.verifier;
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.BufferedReader;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileNotFoundException;
26  import java.io.FileReader;
27  import java.io.FilenameFilter;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.nio.charset.Charset;
33  import java.nio.file.Files;
34  import java.nio.file.Paths;
35  import java.util.ArrayList;
36  import java.util.Collections;
37  import java.util.HashMap;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Properties;
41  import java.util.StringTokenizer;
42  import java.util.regex.Pattern;
43  
44  import javax.xml.parsers.ParserConfigurationException;
45  import javax.xml.parsers.SAXParser;
46  import javax.xml.parsers.SAXParserFactory;
47  
48  import org.apache.maven.shared.utils.StringUtils;
49  import org.apache.maven.shared.utils.io.FileUtils;
50  import org.xml.sax.InputSource;
51  import org.xml.sax.SAXException;
52  import org.xml.sax.SAXParseException;
53  import org.xml.sax.helpers.DefaultHandler;
54  
55  /**
56   * @author Jason van Zyl
57   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
58   */
59  public class Verifier
60  {
61      private static final String LOG_FILENAME = "log.txt";
62  
63      private static final String[] DEFAULT_CLI_ARGUMENTS = {"-e", "--batch-mode"};
64  
65      /**
66       * Command used to clean project before execution.
67       * <p
68       * NOTE: Neither test lifecycle binding nor prefix resolution here but call the goal directly.
69       */
70      private static final String CLEAN_CLI_ARGUMENT = "org.apache.maven.plugins:maven-clean-plugin:clean";
71  
72      private String localRepo;
73  
74      private final String basedir;
75  
76      private final String[] defaultCliArguments;
77  
78      private List<String> cliArguments = new ArrayList<>();
79  
80      private Properties systemProperties = new Properties();
81  
82      private Map<String, String> environmentVariables = new HashMap<>();
83  
84      private Properties verifierProperties = new Properties();
85  
86      private boolean autoclean = true;
87  
88      private String localRepoLayout = "default";
89  
90      /**
91       * If {@code true} uses {@link ForkedLauncher}, if {@code false} uses {@link Embedded3xLauncher},
92       * otherwise considers the value {@link #forkMode}.
93       */
94      private Boolean forkJvm;
95  
96      private String logFileName = LOG_FILENAME;
97  
98      private String mavenHome;
99  
100     // will launch mvn with -X
101     private boolean mavenDebug = false;
102 
103     /**
104      * Either "auto" (use {@link ForkedLauncher} when {@link #environmentVariables} is not empty,
105      * otherwise use {@link Embedded3xLauncher}) , "embedder" (always use {@link Embedded3xLauncher})
106      * or something else (always use {@link ForkedLauncher}).
107      * Set through system property {@code verifier.forkMode}.
108      * Only relevant if {@link #forkJvm} is {@code null}.
109      */
110     private String forkMode;
111 
112     private boolean debugJvm = false;
113 
114     private boolean useWrapper;
115 
116     private static MavenLauncher embeddedLauncher;
117 
118     public Verifier( String basedir )
119         throws VerificationException
120     {
121         this( basedir, null );
122     }
123 
124     public Verifier( String basedir, boolean debug )
125         throws VerificationException
126     {
127         this( basedir, null, debug );
128     }
129 
130     public Verifier( String basedir, String settingsFile )
131         throws VerificationException
132     {
133         this( basedir, settingsFile, false );
134     }
135 
136     public Verifier( String basedir, String settingsFile, boolean debug )
137         throws VerificationException
138     {
139         this( basedir, settingsFile, debug, DEFAULT_CLI_ARGUMENTS );
140     }
141 
142     public Verifier( String basedir, String settingsFile, boolean debug, String[] defaultCliArguments )
143         throws VerificationException
144     {
145         this( basedir, settingsFile, debug, null, defaultCliArguments );
146     }
147 
148     public Verifier( String basedir, String settingsFile, boolean debug, boolean forkJvm )
149         throws VerificationException
150     {
151         this( basedir, settingsFile, debug, forkJvm, DEFAULT_CLI_ARGUMENTS );
152     }
153 
154     public Verifier( String basedir, String settingsFile, boolean debug, boolean forkJvm, String[] defaultCliArguments )
155         throws VerificationException
156     {
157         this( basedir, settingsFile, debug, forkJvm, defaultCliArguments, null );
158     }
159 
160     public Verifier( String basedir, String settingsFile, boolean debug, String mavenHome )
161             throws VerificationException
162     {
163         this( basedir, settingsFile, debug, null, DEFAULT_CLI_ARGUMENTS, mavenHome );
164     }
165 
166     public Verifier( String basedir, String settingsFile, boolean debug, String mavenHome,
167                      String[] defaultCliArguments )
168         throws VerificationException
169     {
170         this( basedir, settingsFile, debug, null, defaultCliArguments, mavenHome );
171     }
172 
173     private Verifier( String basedir, String settingsFile, boolean debug, Boolean forkJvm, String[] defaultCliArguments,
174             String mavenHome ) throws VerificationException
175     {
176         this.basedir = basedir;
177 
178         this.forkJvm = forkJvm;
179         this.forkMode = System.getProperty( "verifier.forkMode" );
180 
181         findLocalRepo( settingsFile );
182         if ( mavenHome == null )
183         {
184             this.mavenHome = System.getProperty( "maven.home" );
185             useWrapper = Files.exists( Paths.get( getBasedir(), "mvnw" ) );
186         }
187         else
188         {
189             this.mavenHome = mavenHome;
190             useWrapper = false;
191         }
192 
193         if ( StringUtils.isEmpty( mavenHome ) && StringUtils.isEmpty( forkMode ) )
194         {
195             forkMode = "auto";
196         }
197 
198         this.defaultCliArguments = defaultCliArguments == null ? new String[0] : defaultCliArguments.clone();
199     }
200 
201     public void setLocalRepo( String localRepo )
202     {
203         this.localRepo = localRepo;
204     }
205 
206     /**
207      * @deprecated will be removed without replacement
208      */
209     @Deprecated
210     public void resetStreams()
211     {
212     }
213 
214 
215     /**
216      * @deprecated will be removed without replacement
217      */
218     @Deprecated
219     public void displayStreamBuffers()
220     {
221     }
222 
223     // ----------------------------------------------------------------------
224     //
225     // ----------------------------------------------------------------------
226 
227     public void verify( boolean chokeOnErrorOutput )
228         throws VerificationException
229     {
230         List<String> lines = loadFile( getBasedir(), "expected-results.txt", false );
231 
232         for ( String line : lines )
233         {
234             verifyExpectedResult( line );
235         }
236 
237         if ( chokeOnErrorOutput )
238         {
239             verifyErrorFreeLog();
240         }
241     }
242 
243     public void verifyErrorFreeLog()
244         throws VerificationException
245     {
246         List<String> lines = loadFile( getBasedir(), getLogFileName(), false );
247 
248         for ( String line : lines )
249         {
250             // A hack to keep stupid velocity resource loader errors from triggering failure
251             if ( stripAnsi( line ).contains( "[ERROR]" ) && !isVelocityError( line ) )
252             {
253                 throw new VerificationException( "Error in execution: " + line );
254             }
255         }
256     }
257 
258     /**
259      * Checks whether the specified line is just an error message from Velocity. Especially old versions of Doxia employ
260      * a very noisy Velocity instance.
261      *
262      * @param line The log line to check, must not be <code>null</code>.
263      * @return <code>true</code> if the line appears to be a Velocity error, <code>false</code> otherwise.
264      */
265     private static boolean isVelocityError( String line )
266     {
267         return line.contains( "VM_global_library.vm" ) || line.contains( "VM #" ) && line.contains( "macro" );
268     }
269 
270     /**
271      * Throws an exception if the text is not present in the log.
272      *
273      * @param text the text to assert present
274      * @throws VerificationException if text is not found in log
275      */
276     public void verifyTextInLog( String text )
277         throws VerificationException
278     {
279         List<String> lines = loadFile( getBasedir(), getLogFileName(), false );
280 
281         boolean result = false;
282         for ( String line : lines )
283         {
284             if ( stripAnsi( line ).contains( text ) )
285             {
286                 result = true;
287                 break;
288             }
289         }
290         if ( !result )
291         {
292             throw new VerificationException( "Text not found in log: " + text );
293         }
294     }
295 
296     public static String stripAnsi( String msg )
297     {
298         return msg.replaceAll( "\u001B\\[[;\\d]*[ -/]*[@-~]", "" );
299     }
300 
301     public Properties loadProperties( String filename )
302         throws VerificationException
303     {
304         Properties properties = new Properties();
305 
306         File propertiesFile = new File( getBasedir(), filename );
307         try ( FileInputStream fis = new FileInputStream( propertiesFile ) )
308         {
309             properties.load( fis );
310         }
311         catch ( IOException e )
312         {
313             throw new VerificationException( "Error reading properties file", e );
314         }
315 
316         return properties;
317     }
318 
319     /**
320      * Loads the (non-empty) lines of the specified text file.
321      *
322      * @param filename The path to the text file to load, relative to the base directory, must not be <code>null</code>.
323      * @param encoding The character encoding of the file, may be <code>null</code> or empty to use the platform default
324      *                 encoding.
325      * @return The list of (non-empty) lines from the text file, can be empty but never <code>null</code>.
326      * @throws IOException If the file could not be loaded.
327      * @since 1.2
328      */
329     public List<String> loadLines( String filename, String encoding )
330         throws IOException
331     {
332         List<String> lines = new ArrayList<>();
333 
334         try ( BufferedReader reader = getReader( filename, encoding ) )
335         {
336             String line;
337             while ( ( line = reader.readLine() ) != null )
338             {
339                 if ( line.length() > 0 )
340                 {
341                     lines.add( line );
342                 }
343             }
344         }
345 
346         return lines;
347     }
348 
349     private BufferedReader getReader( String filename, String encoding ) throws IOException
350     {
351         File file = new File( getBasedir(), filename );
352 
353         if ( StringUtils.isNotEmpty( encoding ) )
354         {
355             return Files.newBufferedReader( file.toPath(), Charset.forName( encoding ) );
356         }
357         else
358         {
359             return Files.newBufferedReader( file.toPath() );
360         }
361     }
362 
363     public List<String> loadFile( String basedir, String filename, boolean hasCommand )
364         throws VerificationException
365     {
366         return loadFile( new File( basedir, filename ), hasCommand );
367     }
368 
369     public List<String> loadFile( File file, boolean hasCommand )
370         throws VerificationException
371     {
372         List<String> lines = new ArrayList<>();
373 
374         if ( file.exists() )
375         {
376             try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) )
377             {
378                 String line = reader.readLine();
379 
380                 while ( line != null )
381                 {
382                     line = line.trim();
383 
384                     if ( !line.startsWith( "#" ) && line.length() != 0 )
385                     {
386                         lines.addAll( replaceArtifacts( line, hasCommand ) );
387                     }
388                     line = reader.readLine();
389                 }
390             }
391             catch ( IOException e )
392             {
393                 throw new VerificationException( e );
394             }
395         }
396 
397         return lines;
398     }
399 
400     private static final String MARKER = "${artifact:";
401 
402     private List<String> replaceArtifacts( String line, boolean hasCommand )
403     {
404         int index = line.indexOf( MARKER );
405         if ( index >= 0 )
406         {
407             String newLine = line.substring( 0, index );
408             index = line.indexOf( "}", index );
409             if ( index < 0 )
410             {
411                 throw new IllegalArgumentException( "line does not contain ending artifact marker: '" + line + "'" );
412             }
413             String artifact = line.substring( newLine.length() + MARKER.length(), index );
414 
415             newLine += getArtifactPath( artifact );
416             newLine += line.substring( index + 1 );
417 
418             List<String> l = new ArrayList<>();
419             l.add( newLine );
420 
421             int endIndex = newLine.lastIndexOf( '/' );
422 
423             String command = null;
424             String filespec;
425             if ( hasCommand )
426             {
427                 int startIndex = newLine.indexOf( ' ' );
428 
429                 command = newLine.substring( 0, startIndex );
430 
431                 filespec = newLine.substring( startIndex + 1, endIndex );
432             }
433             else
434             {
435                 filespec = newLine;
436             }
437 
438             File dir = new File( filespec );
439             addMetadataToList( dir, hasCommand, l, command );
440             addMetadataToList( dir.getParentFile(), hasCommand, l, command );
441 
442             return l;
443         }
444         else
445         {
446             return Collections.singletonList( line );
447         }
448     }
449 
450     private static void addMetadataToList( File dir, boolean hasCommand, List<String> l, String command )
451     {
452         if ( dir.exists() && dir.isDirectory() )
453         {
454             String[] files = dir.list( new FilenameFilter()
455             {
456                 public boolean accept( File dir, String name )
457                 {
458                     return name.startsWith( "maven-metadata" ) && name.endsWith( ".xml" );
459 
460                 }
461             } );
462 
463             for ( String file : files )
464             {
465                 if ( hasCommand )
466                 {
467                     l.add( command + " " + new File( dir, file ).getPath() );
468                 }
469                 else
470                 {
471                     l.add( new File( dir, file ).getPath() );
472                 }
473             }
474         }
475     }
476 
477     private String getArtifactPath( String artifact )
478     {
479         StringTokenizer tok = new StringTokenizer( artifact, ":" );
480         if ( tok.countTokens() != 4 )
481         {
482             throw new IllegalArgumentException( "Artifact must have 4 tokens: '" + artifact + "'" );
483         }
484 
485         String[] a = new String[4];
486         for ( int i = 0; i < 4; i++ )
487         {
488             a[i] = tok.nextToken();
489         }
490 
491         String groupId = a[0];
492         String artifactId = a[1];
493         String version = a[2];
494         String ext = a[3];
495         return getArtifactPath( groupId, artifactId, version, ext );
496     }
497 
498     public String getArtifactPath( String groupId, String artifactId, String version, String ext )
499     {
500         return getArtifactPath( groupId, artifactId, version, ext, null );
501     }
502 
503     /**
504      * Returns the absolute path to the artifact denoted by groupId, artifactId, version, extension and classifier.
505      *
506      * @param gid        The groupId, must not be null.
507      * @param aid        The artifactId, must not be null.
508      * @param version    The version, must not be null.
509      * @param ext        The extension, must not be null.
510      * @param classifier The classifier, may be null to be omitted.
511      * @return the absolute path to the artifact denoted by groupId, artifactId, version, extension and classifier,
512      *         never null.
513      */
514     public String getArtifactPath( String gid, String aid, String version, String ext, String classifier )
515     {
516         if ( classifier != null && classifier.length() == 0 )
517         {
518             classifier = null;
519         }
520         if ( "maven-plugin".equals( ext ) )
521         {
522             ext = "jar";
523         }
524         if ( "coreit-artifact".equals( ext ) )
525         {
526             ext = "jar";
527             classifier = "it";
528         }
529         if ( "test-jar".equals( ext ) )
530         {
531             ext = "jar";
532             classifier = "tests";
533         }
534 
535         String repositoryPath;
536         if ( "legacy".equals( localRepoLayout ) )
537         {
538             repositoryPath = gid + "/" + ext + "s/" + aid + "-" + version + "." + ext;
539         }
540         else if ( "default".equals( localRepoLayout ) )
541         {
542             repositoryPath = gid.replace( '.', '/' );
543             repositoryPath = repositoryPath + "/" + aid + "/" + version;
544             repositoryPath = repositoryPath + "/" + aid + "-" + version;
545             if ( classifier != null )
546             {
547                 repositoryPath = repositoryPath + "-" + classifier;
548             }
549             repositoryPath = repositoryPath + "." + ext;
550         }
551         else
552         {
553             throw new IllegalStateException( "Unknown layout: " + localRepoLayout );
554         }
555 
556         return localRepo + "/" + repositoryPath;
557     }
558 
559     public List<String> getArtifactFileNameList( String org, String name, String version, String ext )
560     {
561         List<String> files = new ArrayList<>();
562         String artifactPath = getArtifactPath( org, name, version, ext );
563         File dir = new File( artifactPath );
564         files.add( artifactPath );
565         addMetadataToList( dir, false, files, null );
566         addMetadataToList( dir.getParentFile(), false, files, null );
567         return files;
568     }
569 
570     /**
571      * Gets the path to the local artifact metadata. Note that the method does not check whether the returned path
572      * actually points to existing metadata.
573      *
574      * @param gid     The group id, must not be <code>null</code>.
575      * @param aid     The artifact id, must not be <code>null</code>.
576      * @param version The artifact version, may be <code>null</code>.
577      * @return The (absolute) path to the local artifact metadata, never <code>null</code>.
578      */
579     public String getArtifactMetadataPath( String gid, String aid, String version )
580     {
581         return getArtifactMetadataPath( gid, aid, version, "maven-metadata-local.xml" );
582     }
583 
584     /**
585      * Gets the path to a file in the local artifact directory. Note that the method does not check whether the returned
586      * path actually points to an existing file.
587      *
588      * @param gid      The group id, must not be <code>null</code>.
589      * @param aid      The artifact id, may be <code>null</code>.
590      * @param version  The artifact version, may be <code>null</code>.
591      * @param filename The filename to use, must not be <code>null</code>.
592      * @return The (absolute) path to the local artifact metadata, never <code>null</code>.
593      */
594     public String getArtifactMetadataPath( String gid, String aid, String version, String filename )
595     {
596         StringBuilder buffer = new StringBuilder( 256 );
597 
598         buffer.append( localRepo );
599         buffer.append( '/' );
600 
601         if ( "default".equals( localRepoLayout ) )
602         {
603             buffer.append( gid.replace( '.', '/' ) );
604             buffer.append( '/' );
605 
606             if ( aid != null )
607             {
608                 buffer.append( aid );
609                 buffer.append( '/' );
610 
611                 if ( version != null )
612                 {
613                     buffer.append( version );
614                     buffer.append( '/' );
615                 }
616             }
617 
618             buffer.append( filename );
619         }
620         else
621         {
622             throw new IllegalStateException( "Unsupported repository layout: " + localRepoLayout );
623         }
624 
625         return buffer.toString();
626     }
627 
628     /**
629      * Gets the path to the local artifact metadata. Note that the method does not check whether the returned path
630      * actually points to existing metadata.
631      *
632      * @param gid The group id, must not be <code>null</code>.
633      * @param aid The artifact id, must not be <code>null</code>.
634      * @return The (absolute) path to the local artifact metadata, never <code>null</code>.
635      */
636     public String getArtifactMetadataPath( String gid, String aid )
637     {
638         return getArtifactMetadataPath( gid, aid, null );
639     }
640 
641     private static String retrieveLocalRepo( String settingsXmlPath )
642         throws VerificationException
643     {
644         UserModelReader userModelReader = new UserModelReader();
645 
646         String userHome = System.getProperty( "user.home" );
647 
648         File userXml;
649 
650         String repo = null;
651 
652         if ( settingsXmlPath != null )
653         {
654             userXml = new File( settingsXmlPath );
655         }
656         else
657         {
658             userXml = new File( userHome, ".m2/settings.xml" );
659         }
660 
661         if ( userXml.exists() )
662         {
663             userModelReader.parse( userXml );
664 
665             String localRepository = userModelReader.getLocalRepository();
666             if ( localRepository != null )
667             {
668                 repo = new File( localRepository ).getAbsolutePath();
669             }
670         }
671 
672         return repo;
673     }
674 
675     public void deleteArtifact( String org, String name, String version, String ext )
676         throws IOException
677     {
678         List<String> files = getArtifactFileNameList( org, name, version, ext );
679         for ( String fileName : files )
680         {
681             FileUtils.forceDelete( new File( fileName ) );
682         }
683     }
684 
685     /**
686      * Deletes all artifacts in the specified group id from the local repository.
687      *
688      * @param gid The group id whose artifacts should be deleted, must not be <code>null</code>.
689      * @throws IOException If the artifacts could not be deleted.
690      * @since 1.2
691      */
692     public void deleteArtifacts( String gid )
693         throws IOException
694     {
695         String path;
696         if ( "default".equals( localRepoLayout ) )
697         {
698             path = gid.replace( '.', '/' );
699         }
700         else if ( "legacy".equals( localRepoLayout ) )
701         {
702             path = gid;
703         }
704         else
705         {
706             throw new IllegalStateException( "Unsupported repository layout: " + localRepoLayout );
707         }
708 
709         FileUtils.deleteDirectory( new File( localRepo, path ) );
710     }
711 
712     /**
713      * Deletes all artifacts in the specified g:a:v from the local repository.
714      *
715      * @param gid     The group id whose artifacts should be deleted, must not be <code>null</code>.
716      * @param aid     The artifact id whose artifacts should be deleted, must not be <code>null</code>.
717      * @param version The (base) version whose artifacts should be deleted, must not be <code>null</code>.
718      * @throws IOException If the artifacts could not be deleted.
719      * @since 1.3
720      */
721     public void deleteArtifacts( String gid, String aid, String version )
722         throws IOException
723     {
724         String path;
725         if ( "default".equals( localRepoLayout ) )
726         {
727             path = gid.replace( '.', '/' ) + '/' + aid + '/' + version;
728         }
729         else
730         {
731             throw new IllegalStateException( "Unsupported repository layout: " + localRepoLayout );
732         }
733 
734         FileUtils.deleteDirectory( new File( localRepo, path ) );
735     }
736 
737     /**
738      * Deletes the specified directory.
739      *
740      * @param path The path to the directory to delete, relative to the base directory, must not be <code>null</code>.
741      * @throws IOException If the directory could not be deleted.
742      * @since 1.2
743      */
744     public void deleteDirectory( String path )
745         throws IOException
746     {
747         FileUtils.deleteDirectory( new File( getBasedir(), path ) );
748     }
749 
750     /**
751      * Writes a text file with the specified contents. The contents will be encoded using UTF-8.
752      *
753      * @param path     The path to the file, relative to the base directory, must not be <code>null</code>.
754      * @param contents The contents to write, must not be <code>null</code>.
755      * @throws IOException If the file could not be written.
756      * @since 1.2
757      */
758     public void writeFile( String path, String contents )
759         throws IOException
760     {
761         FileUtils.fileWrite( new File( getBasedir(), path ).getAbsolutePath(), "UTF-8", contents );
762     }
763 
764     /**
765      * Filters a text file by replacing some user-defined tokens.
766      * This method is equivalent to:
767      *
768      * <pre>
769      *     filterFile( srcPath, dstPath, fileEncoding, verifier.newDefaultFilterMap() )
770      * </pre>
771      *
772      * @param srcPath          The path to the input file, relative to the base directory, must not be
773      *                         <code>null</code>.
774      * @param dstPath          The path to the output file, relative to the base directory and possibly equal to the
775      *                         input file, must not be <code>null</code>.
776      * @param fileEncoding     The file encoding to use, may be <code>null</code> or empty to use the platform's default
777      *                         encoding.
778      * @return The path to the filtered output file, never <code>null</code>.
779      * @throws IOException If the file could not be filtered.
780      * @since 2.0
781      */
782     public File filterFile( String srcPath, String dstPath, String fileEncoding )
783         throws IOException
784     {
785         return filterFile( srcPath, dstPath, fileEncoding, newDefaultFilterMap() );
786     }
787 
788     /**
789      * Filters a text file by replacing some user-defined tokens.
790      *
791      * @param srcPath      The path to the input file, relative to the base directory, must not be
792      *                     <code>null</code>.
793      * @param dstPath      The path to the output file, relative to the base directory and possibly equal to the
794      *                     input file, must not be <code>null</code>.
795      * @param fileEncoding The file encoding to use, may be <code>null</code> or empty to use the platform's default
796      *                     encoding.
797      * @param filterMap    The mapping from tokens to replacement values, must not be <code>null</code>.
798      * @return The path to the filtered output file, never <code>null</code>.
799      * @throws IOException If the file could not be filtered.
800      * @since 1.2
801      */
802     public File filterFile( String srcPath, String dstPath, String fileEncoding, Map<String, String> filterMap )
803         throws IOException
804     {
805         File srcFile = new File( getBasedir(), srcPath );
806         String data = FileUtils.fileRead( srcFile, fileEncoding );
807 
808         for ( Map.Entry<String, String> entry : filterMap.entrySet() )
809         {
810             data = StringUtils.replace( data, entry.getKey() , entry.getValue() );
811         }
812 
813         File dstFile = new File( getBasedir(), dstPath );
814         //noinspection ResultOfMethodCallIgnored
815         dstFile.getParentFile().mkdirs();
816         FileUtils.fileWrite( dstFile.getPath(), fileEncoding, data );
817 
818         return dstFile;
819     }
820 
821     /**
822      * There are 226 references to this method in Maven core ITs. In most (all?) cases it is used together with
823      * {@link #newDefaultFilterProperties()}. Need to remove both methods and update all clients eventually/
824      *
825      * @param srcPath          The path to the input file, relative to the base directory, must not be
826      *                         <code>null</code>.
827      * @param dstPath          The path to the output file, relative to the base directory and possibly equal to the
828      *                         input file, must not be <code>null</code>.
829      * @param fileEncoding     The file encoding to use, may be <code>null</code> or empty to use the platform's default
830      *                         encoding.
831      * @param filterProperties The mapping from tokens to replacement values, must not be <code>null</code>.
832      * @return The path to the filtered output file, never <code>null</code>.
833      * @throws IOException If the file could not be filtered.
834      * @deprecated use {@link #filterFile(String, String, String, Map)}
835      */
836     @Deprecated
837     @SuppressWarnings( { "rawtypes", "unchecked" } )
838     public File filterFile( String srcPath, String dstPath, String fileEncoding, Properties filterProperties )
839         throws IOException
840     {
841         return filterFile( srcPath, dstPath, fileEncoding, (Map) filterProperties );
842     }
843 
844     /**
845      * Gets a new copy of the default filter properties. These default filter properties map the tokens "@basedir@" and
846      * "@baseurl@" to the test's base directory and its base <code>file:</code> URL, respectively.
847      *
848      * @return The (modifiable) map with the default filter properties, never <code>null</code>.
849      * @since 1.2
850      * @deprecated use {@link #newDefaultFilterMap()}
851      */
852     @Deprecated
853     public Properties newDefaultFilterProperties()
854     {
855         Properties filterProperties = new Properties();
856         filterProperties.putAll( newDefaultFilterMap() );
857         return filterProperties;
858     }
859 
860     /**
861      * Gets a new copy of the default filter map. These default filter map, contains the tokens "@basedir@" and
862      * "@baseurl@" to the test's base directory and its base <code>file:</code> URL, respectively.
863      *
864      * @return The (modifiable) map with the default filter map, never <code>null</code>.
865      * @since 2.0
866      */
867     public Map<String, String> newDefaultFilterMap()
868     {
869         Map<String, String> filterMap = new HashMap<>();
870 
871         String basedir = new File( getBasedir() ).getAbsolutePath();
872         filterMap.put( "@basedir@", basedir );
873 
874         /*
875          * NOTE: Maven fails to properly handle percent-encoded "file:" URLs (WAGON-111) so don't use File.toURI() here
876          * and just do it the simple way.
877          */
878         String baseurl = basedir;
879         if ( !baseurl.startsWith( "/" ) )
880         {
881             baseurl = '/' + baseurl;
882         }
883         baseurl = "file://" + baseurl.replace( '\\', '/' );
884         filterMap.put( "@baseurl@", baseurl );
885 
886         return filterMap;
887     }
888 
889     /**
890      * Verifies that the given file exists.
891      *
892      * @param file the path of the file to check
893      * @throws VerificationException in case the given file does not exist
894      */
895     public void verifyFilePresent( String file ) throws VerificationException
896     {
897         verifyFilePresence( file, true );
898     }
899 
900     /**
901      * Verifies the given file's content matches an regular expression.
902      * Note this method also checks that the file exists and is readable.
903      *
904      * @param file the path of the file to check
905      * @param regex a regular expression
906      * @throws VerificationException in case the file was not found or its content does not match the given pattern
907      * @see Pattern
908      */
909     public void verifyFileContentMatches( String file, String regex ) throws VerificationException
910     {
911         verifyFilePresent( file );
912         try
913         {
914             String content = FileUtils.fileRead( file );
915             if ( !Pattern.matches( regex, content ) )
916             {
917                 throw new VerificationException( "Content of " + file + " does not match " + regex );
918             }
919         }
920         catch ( IOException e )
921         {
922             throw new VerificationException( "Could not read from " + file, e );
923         }
924     }
925 
926     /**
927      * Verifies that the given file does not exist.
928      *
929      * @param file the path of the file to check
930      * @throws VerificationException if the given file exists
931      */
932     public void verifyFileNotPresent( String file ) throws VerificationException
933     {
934         verifyFilePresence( file, false );
935     }
936 
937     private void verifyArtifactPresence( boolean wanted, String groupId, String artifactId, String version, String ext )
938                     throws VerificationException
939     {
940         List<String> files = getArtifactFileNameList( groupId, artifactId, version, ext );
941         for ( String fileName : files )
942         {
943             verifyFilePresence( fileName, wanted );
944         }
945     }
946 
947     /**
948      * Verifies that the artifact given through its Maven coordinates exists.
949      *
950      * @param groupId the groupId of the artifact (must not be null)
951      * @param artifactId the artifactId of the artifact (must not be null)
952      * @param version the version of the artifact (must not be null)
953      * @param ext the extension of the artifact (must not be null)
954      * @throws VerificationException if the given artifact does not exist
955      */
956     public void verifyArtifactPresent( String groupId, String artifactId, String version, String ext )
957                     throws VerificationException
958     {
959         verifyArtifactPresence( true, groupId, artifactId, version, ext );
960     }
961 
962     /**
963      * Verifies that the artifact given through its Maven coordinates does not exist.
964      *
965      * @param groupId the groupId of the artifact (must not be null)
966      * @param artifactId the artifactId of the artifact (must not be null)
967      * @param version the version of the artifact (must not be null)
968      * @param ext the extension of the artifact (must not be null)
969      * @throws VerificationException if the given artifact exists
970      */
971     public void verifyArtifactNotPresent( String groupId, String artifactId, String version, String ext )
972                     throws VerificationException
973     {
974         verifyArtifactPresence( false, groupId, artifactId, version, ext );
975     }
976 
977     private void verifyExpectedResult( String line )
978         throws VerificationException
979     {
980         boolean wanted = true;
981         if ( line.startsWith( "!" ) )
982         {
983             line = line.substring( 1 );
984             wanted = false;
985         }
986 
987         verifyFilePresence( line, wanted );
988     }
989 
990     private void verifyFilePresence( String filePath, boolean wanted )
991         throws VerificationException
992     {
993         if ( filePath.indexOf( "!/" ) > 0 )
994         {
995             String urlString = "jar:file:" + getBasedir() + "/" + filePath;
996 
997             InputStream is = null;
998             try
999             {
1000                 URL url = new URL( urlString );
1001 
1002                 is = url.openStream();
1003 
1004                 if ( is == null )
1005                 {
1006                     if ( wanted )
1007                     {
1008                         throw new VerificationException( "Expected JAR resource was not found: " + filePath );
1009                     }
1010                 }
1011                 else
1012                 {
1013                     if ( !wanted )
1014                     {
1015                         throw new VerificationException( "Unwanted JAR resource was found: " + filePath );
1016                     }
1017                 }
1018             }
1019             catch ( MalformedURLException e )
1020             {
1021                 throw new VerificationException( "Error looking for JAR resource", e );
1022             }
1023             catch ( IOException e )
1024             {
1025                 if ( wanted )
1026                 {
1027                     throw new VerificationException( "Error looking for JAR resource: " + filePath );
1028                 }
1029             }
1030             finally
1031             {
1032                 if ( is != null )
1033                 {
1034                     try
1035                     {
1036                         is.close();
1037                     }
1038                     catch ( IOException e )
1039                     {
1040                         // ignore
1041                     }
1042                 }
1043             }
1044         }
1045         else
1046         {
1047             File expectedFile = new File( filePath );
1048 
1049             // NOTE: On Windows, a path with a leading (back-)slash is relative to the current drive
1050             if ( !expectedFile.isAbsolute() && !expectedFile.getPath().startsWith( File.separator ) )
1051             {
1052                 expectedFile = new File( getBasedir(), filePath );
1053             }
1054 
1055             if ( filePath.indexOf( '*' ) > -1 )
1056             {
1057                 File parent = expectedFile.getParentFile();
1058 
1059                 if ( !parent.exists() )
1060                 {
1061                     if ( wanted )
1062                     {
1063                         throw new VerificationException(
1064                             "Expected file pattern was not found: " + expectedFile.getPath() );
1065                     }
1066                 }
1067                 else
1068                 {
1069                     String shortNamePattern = expectedFile.getName().replaceAll( "\\*", ".*" );
1070 
1071                     String[] candidates = parent.list();
1072 
1073                     boolean found = false;
1074 
1075                     if ( candidates != null )
1076                     {
1077                         for ( String candidate : candidates )
1078                         {
1079                             if ( candidate.matches( shortNamePattern ) )
1080                             {
1081                                 found = true;
1082                                 break;
1083                             }
1084                         }
1085                     }
1086 
1087                     if ( !found && wanted )
1088                     {
1089                         throw new VerificationException(
1090                             "Expected file pattern was not found: " + expectedFile.getPath() );
1091                     }
1092                     else if ( found && !wanted )
1093                     {
1094                         throw new VerificationException( "Unwanted file pattern was found: " + expectedFile.getPath() );
1095                     }
1096                 }
1097             }
1098             else
1099             {
1100                 if ( !expectedFile.exists() )
1101                 {
1102                     if ( wanted )
1103                     {
1104                         throw new VerificationException( "Expected file was not found: " + expectedFile.getPath() );
1105                     }
1106                 }
1107                 else
1108                 {
1109                     if ( !wanted )
1110                     {
1111                         throw new VerificationException( "Unwanted file was found: " + expectedFile.getPath() );
1112                     }
1113                 }
1114             }
1115         }
1116     }
1117 
1118     // ----------------------------------------------------------------------
1119     //
1120     // ----------------------------------------------------------------------
1121 
1122     /**
1123      * Execute Maven.
1124      *
1125      * @deprecated will be removed.
1126      * <p>
1127      * For replacement please use:
1128      * <pre>
1129      *   verifier.addCliArgument( "goal" );
1130      *   verifier.execute();
1131      * </pre>
1132      */
1133     @Deprecated
1134     public void executeGoal( String goal )
1135         throws VerificationException
1136     {
1137         executeGoal( goal, environmentVariables );
1138     }
1139 
1140     /**
1141      * Execute Maven.
1142      *
1143      * @deprecated will be removed.
1144      * <p>
1145      * For replacement please use:
1146      * <pre>
1147      *   verifier.addCliArgument( "goal" );
1148      *   verifier.setEnvironmentVariable( "key1", "value1" );
1149      *   verifier.setEnvironmentVariable( "key2", "value2" );
1150      *   verifier.execute();
1151      * </pre>
1152      */
1153     @Deprecated
1154     public void executeGoal( String goal, Map<String, String> envVars )
1155         throws VerificationException
1156     {
1157         executeGoals( Collections.singletonList( goal ), envVars );
1158     }
1159 
1160     /**
1161      * Execute Maven.
1162      *
1163      * @deprecated will be removed
1164      * <p>
1165      * For replacement please use:
1166      * <pre>
1167      *   verifier.addCliArguments( "goal1", "goal2" );
1168      *   verifier.execute();
1169      * </pre>
1170      */
1171     @Deprecated
1172     public void executeGoals( List<String> goals )
1173         throws VerificationException
1174     {
1175         executeGoals( goals, environmentVariables );
1176     }
1177 
1178     public String getExecutable()
1179     {
1180         // Use a strategy for finding the maven executable, John has a simple method like this
1181         // but a little strategy + chain of command would be nicer.
1182 
1183         if ( mavenHome != null )
1184         {
1185             return mavenHome + "/bin/mvn";
1186         }
1187         else
1188         {
1189             return "mvn";
1190         }
1191     }
1192 
1193     /**
1194      * Execute Maven.
1195      *
1196      * @deprecated will be removed
1197      * <p>
1198      * For replacement please use:
1199      * <pre>
1200      *   verifier.addCliArguments( "goal1", "goal2" );
1201      *   verifier.setEnvironmentVariable( "key1", "value1" );
1202      *   verifier.setEnvironmentVariable( "key2", "value2" );
1203      *   verifier.execute();
1204      * </pre>
1205      */
1206     @Deprecated
1207     public void executeGoals( List<String> goals, Map<String, String> envVars )
1208         throws VerificationException
1209     {
1210         cliArguments.addAll( goals );
1211         environmentVariables.putAll( envVars );
1212         execute();
1213     }
1214 
1215     /**
1216      * Execute Maven.
1217      */
1218     public void execute() throws VerificationException
1219     {
1220 
1221         List<String> args = new ArrayList<>();
1222 
1223         Collections.addAll( args, defaultCliArguments );
1224 
1225         if ( this.mavenDebug )
1226         {
1227             args.add( "-X" );
1228         }
1229 
1230         /*
1231          * NOTE: Unless explicitly requested by the caller, the forked builds should use the current local
1232          * repository. Otherwise, the forked builds would in principle leave the sandbox environment which has been
1233          * setup for the current build. In particular, using "maven.repo.local" will make sure the forked builds use
1234          * the same local repo as the parent build even if a custom user settings is provided.
1235          */
1236         boolean useMavenRepoLocal = Boolean.valueOf( verifierProperties.getProperty( "use.mavenRepoLocal", "true" ) );
1237 
1238         if ( useMavenRepoLocal )
1239         {
1240             args.add( "-Dmaven.repo.local=" + localRepo );
1241         }
1242 
1243         if ( autoclean )
1244         {
1245             args.add( CLEAN_CLI_ARGUMENT );
1246         }
1247 
1248         for ( String cliArgument : cliArguments )
1249         {
1250             args.add( cliArgument.replace( "${basedir}", getBasedir() ) );
1251         }
1252 
1253         int ret;
1254         File logFile = new File( getBasedir(), getLogFileName() );
1255 
1256         try
1257         {
1258             MavenLauncher launcher = getMavenLauncher( environmentVariables );
1259 
1260             String[] cliArgs = args.toArray( new String[0] );
1261             ret = launcher.run( cliArgs, systemProperties, getBasedir(), logFile );
1262         }
1263         catch ( LauncherException e )
1264         {
1265             throw new VerificationException( "Failed to execute Maven", e );
1266         }
1267         catch ( IOException e )
1268         {
1269             throw new VerificationException( e );
1270         }
1271 
1272         if ( ret > 0 )
1273         {
1274             throw new VerificationException(
1275                 "Exit code was non-zero: " + ret + "; command line and log = \n" + new File( mavenHome,
1276                                                                                              "bin/mvn" ) + " "
1277                     + StringUtils.join( args.iterator(), " " ) + "\n" + getLogContents( logFile ) );
1278         }
1279     }
1280 
1281     protected MavenLauncher getMavenLauncher( Map<String, String> envVars )
1282         throws LauncherException
1283     {
1284         boolean fork;
1285         if ( useWrapper )
1286         {
1287             fork = true;
1288         }
1289         else if ( forkJvm != null )
1290         {
1291             fork = forkJvm;
1292         }
1293         else if ( ( envVars.isEmpty() && "auto".equalsIgnoreCase( forkMode ) )
1294             || "embedded".equalsIgnoreCase( forkMode ) )
1295         {
1296             fork = false;
1297 
1298             try
1299             {
1300                 initEmbeddedLauncher();
1301             }
1302             catch ( Exception e )
1303             {
1304                 fork = true;
1305             }
1306         }
1307         else
1308         {
1309             fork = true;
1310         }
1311 
1312         if ( !fork )
1313         {
1314             if ( !envVars.isEmpty() )
1315             {
1316                 throw new LauncherException( "Environment variables are not supported in embedded runtime" );
1317             }
1318 
1319             initEmbeddedLauncher();
1320 
1321             return embeddedLauncher;
1322         }
1323         else
1324         {
1325             return new ForkedLauncher( mavenHome, envVars, debugJvm, useWrapper );
1326         }
1327     }
1328 
1329     private void initEmbeddedLauncher()
1330         throws LauncherException
1331     {
1332         if ( embeddedLauncher == null )
1333         {
1334             if ( StringUtils.isEmpty( mavenHome ) )
1335             {
1336                 embeddedLauncher = Embedded3xLauncher.createFromClasspath();
1337             }
1338             else
1339             {
1340                 String defaultClasspath = System.getProperty( "maven.bootclasspath" );
1341                 String defaultClassworldConf = System.getProperty( "classworlds.conf" );
1342                 embeddedLauncher = Embedded3xLauncher.createFromMavenHome( mavenHome, defaultClassworldConf,
1343                         parseClasspath( defaultClasspath ) );
1344             }
1345         }
1346     }
1347 
1348     private static List<URL> parseClasspath( String classpath )
1349         throws LauncherException
1350     {
1351         if ( classpath == null )
1352         {
1353             return null;
1354         }
1355         ArrayList<URL> classpathUrls = new ArrayList<>();
1356         StringTokenizer st = new StringTokenizer( classpath, File.pathSeparator );
1357         while ( st.hasMoreTokens() )
1358         {
1359             try
1360             {
1361                 classpathUrls.add( new File( st.nextToken() ).toURI().toURL() );
1362             }
1363             catch ( MalformedURLException e )
1364             {
1365                 throw new LauncherException( "Invalid launcher classpath " + classpath, e );
1366             }
1367         }
1368         return classpathUrls;
1369     }
1370 
1371     public String getMavenVersion()
1372         throws VerificationException
1373     {
1374         try
1375         {
1376             return getMavenLauncher( Collections.emptyMap() ).getMavenVersion();
1377         }
1378         catch ( LauncherException | IOException e )
1379         {
1380             throw new VerificationException( e );
1381         }
1382     }
1383 
1384     private static String getLogContents( File logFile )
1385     {
1386         try
1387         {
1388             return FileUtils.fileRead( logFile );
1389         }
1390         catch ( IOException e )
1391         {
1392             // ignore
1393             return "(Error reading log contents: " + e.getMessage() + ")";
1394         }
1395     }
1396 
1397     private void findLocalRepo( String settingsFile )
1398         throws VerificationException
1399     {
1400         if ( localRepo == null )
1401         {
1402             localRepo = System.getProperty( "maven.repo.local" );
1403         }
1404 
1405         if ( localRepo == null )
1406         {
1407             localRepo = retrieveLocalRepo( settingsFile );
1408         }
1409 
1410         if ( localRepo == null )
1411         {
1412             localRepo = System.getProperty( "user.home" ) + "/.m2/repository";
1413         }
1414 
1415         File repoDir = new File( localRepo );
1416 
1417         if ( !repoDir.exists() )
1418         {
1419             //noinspection ResultOfMethodCallIgnored
1420             repoDir.mkdirs();
1421         }
1422 
1423         // normalize path
1424         localRepo = repoDir.getAbsolutePath();
1425 
1426         localRepoLayout = System.getProperty( "maven.repo.local.layout", "default" );
1427     }
1428 
1429     /**
1430      * Verifies that the artifact given by its Maven coordinates exists and contains the given content.
1431      *
1432      * @param groupId the groupId of the artifact (must not be null)
1433      * @param artifactId the artifactId of the artifact (must not be null)
1434      * @param version the version of the artifact (must not be null)
1435      * @param ext the extension of the artifact (must not be null)
1436      * @param content the expected content
1437      * @throws IOException if reading from the artifact fails
1438      * @throws VerificationException if the content of the artifact differs
1439      */
1440     public void verifyArtifactContent( String groupId, String artifactId, String version, String ext, String content )
1441         throws IOException, VerificationException
1442     {
1443         String fileName = getArtifactPath( groupId, artifactId, version, ext );
1444         if ( !content.equals( FileUtils.fileRead( fileName ) ) )
1445         {
1446             throw new VerificationException( "Content of " + fileName + " does not equal " + content );
1447         }
1448     }
1449 
1450     static class UserModelReader
1451         extends DefaultHandler
1452     {
1453         private String localRepository;
1454 
1455         private StringBuilder currentBody = new StringBuilder();
1456 
1457         public void parse( File file )
1458             throws VerificationException
1459         {
1460             try
1461             {
1462                 SAXParserFactory saxFactory = SAXParserFactory.newInstance();
1463 
1464                 SAXParser parser = saxFactory.newSAXParser();
1465 
1466                 InputSource is = new InputSource( new FileInputStream( file ) );
1467 
1468                 parser.parse( is, this );
1469             }
1470             catch ( FileNotFoundException e )
1471             {
1472                 throw new VerificationException( "file not found path : " + file.getAbsolutePath(), e );
1473             }
1474             catch ( IOException e )
1475             {
1476                 throw new VerificationException( " IOException path : " + file.getAbsolutePath(), e );
1477             }
1478             catch ( ParserConfigurationException e )
1479             {
1480                 throw new VerificationException( e );
1481             }
1482             catch ( SAXException e )
1483             {
1484                 throw new VerificationException( "Parsing exception for file " + file.getAbsolutePath(), e );
1485             }
1486         }
1487 
1488         public void warning( SAXParseException spe )
1489         {
1490             // ignore warnings
1491         }
1492 
1493         public void error( SAXParseException spe ) throws SAXException
1494         {
1495             throw new SAXException( spe );
1496         }
1497 
1498         public void fatalError( SAXParseException spe ) throws SAXException
1499         {
1500             throw new SAXException( spe );
1501         }
1502 
1503         public String getLocalRepository()
1504         {
1505             return localRepository;
1506         }
1507 
1508         public void characters( char[] ch, int start, int length )
1509             throws SAXException
1510         {
1511             currentBody.append( ch, start, length );
1512         }
1513 
1514         public void endElement( String uri, String localName, String rawName )
1515             throws SAXException
1516         {
1517             if ( "localRepository".equals( rawName ) )
1518             {
1519                 if ( notEmpty( currentBody.toString() ) )
1520                 {
1521                     localRepository = currentBody.toString().trim();
1522                 }
1523                 else
1524                 {
1525                     throw new SAXException(
1526                         "Invalid mavenProfile entry. Missing one or more " + "fields: {localRepository}." );
1527                 }
1528             }
1529 
1530             currentBody = new StringBuilder();
1531         }
1532 
1533         private boolean notEmpty( String test )
1534         {
1535             return test != null && test.trim().length() > 0;
1536         }
1537 
1538         public void reset()
1539         {
1540             currentBody = null;
1541             localRepository = null;
1542         }
1543     }
1544 
1545     /**
1546      * @deprecated will be removed without replacement,
1547      * for arguments adding please use {@link #addCliArgument(String)}, {@link #addCliArguments(String...)}
1548      */
1549     @Deprecated
1550     public List<String> getCliOptions()
1551     {
1552         return cliArguments;
1553     }
1554 
1555     /**
1556      * @deprecated will be removed
1557      */
1558     @Deprecated
1559     public void setCliOptions( List<String> cliOptions )
1560     {
1561         this.cliArguments = cliOptions;
1562     }
1563 
1564     /**
1565      * Add a command line argument, each argument must be set separately one by one.
1566      * <p>
1567      * <code>${basedir}</code> in argument will be replaced by value of {@link #getBasedir()} during execution.
1568      * @param option an argument to add
1569      * @deprecated please use {@link #addCliArgument(String)}
1570      */
1571     @Deprecated
1572     public void addCliOption( String option )
1573     {
1574         addCliArgument( option );
1575     }
1576 
1577     /**
1578      * Add a command line argument, each argument must be set separately one by one.
1579      * <p>
1580      * <code>${basedir}</code> in argument will be replaced by value of {@link #getBasedir()} during execution.
1581      *
1582      * @param cliArgument an argument to add
1583      */
1584     public void addCliArgument( String cliArgument )
1585     {
1586         cliArguments.add( cliArgument );
1587     }
1588 
1589     /**
1590      * Add a command line arguments, each argument must be set separately one by one.
1591      * <p>
1592      * <code>${basedir}</code> in argument will be replaced by value of {@link #getBasedir()} during execution.
1593      *
1594      * @param cliArguments an arguments list to add
1595      */
1596     public void addCliArguments( String... cliArguments )
1597     {
1598         Collections.addAll( this.cliArguments, cliArguments );
1599     }
1600 
1601 
1602     public Properties getSystemProperties()
1603     {
1604         return systemProperties;
1605     }
1606 
1607     public void setSystemProperties( Properties systemProperties )
1608     {
1609         this.systemProperties = systemProperties;
1610     }
1611 
1612     public void setSystemProperty( String key, String value )
1613     {
1614         if ( value != null )
1615         {
1616             systemProperties.setProperty( key, value );
1617         }
1618         else
1619         {
1620             systemProperties.remove( key );
1621         }
1622     }
1623 
1624     public Map<String, String> getEnvironmentVariables()
1625     {
1626         return environmentVariables;
1627     }
1628 
1629     public void setEnvironmentVariables( Map<String, String> environmentVariables )
1630     {
1631         this.environmentVariables = environmentVariables;
1632     }
1633 
1634     public void setEnvironmentVariable( String key, String value )
1635     {
1636         if ( value != null )
1637         {
1638             environmentVariables.put( key, value );
1639         }
1640         else
1641         {
1642             environmentVariables.remove( key );
1643         }
1644     }
1645 
1646     public Properties getVerifierProperties()
1647     {
1648         return verifierProperties;
1649     }
1650 
1651     public void setVerifierProperties( Properties verifierProperties )
1652     {
1653         this.verifierProperties = verifierProperties;
1654     }
1655 
1656     public boolean isAutoclean()
1657     {
1658         return autoclean;
1659     }
1660 
1661     /**
1662      * Clean project before execution by adding {@link #CLEAN_CLI_ARGUMENT} to command line.
1663      * <p>
1664      * By default, options is enabled.
1665      *
1666      * @param autoclean indicate if option is enabled
1667      */
1668     public void setAutoclean( boolean autoclean )
1669     {
1670         this.autoclean = autoclean;
1671     }
1672 
1673     public String getBasedir()
1674     {
1675         return basedir;
1676     }
1677 
1678     /**
1679      * Gets the name of the file used to log build output.
1680      *
1681      * @return The name of the log file, relative to the base directory, never <code>null</code>.
1682      * @since 1.2
1683      */
1684     public String getLogFileName()
1685     {
1686         return this.logFileName;
1687     }
1688 
1689     /**
1690      * Sets the name of the file used to log build output.
1691      *
1692      * @param logFileName The name of the log file, relative to the base directory, must not be empty or
1693      *                    <code>null</code>.
1694      * @since 1.2
1695      */
1696     public void setLogFileName( String logFileName )
1697     {
1698         if ( StringUtils.isEmpty( logFileName ) )
1699         {
1700             throw new IllegalArgumentException( "log file name unspecified" );
1701         }
1702         this.logFileName = logFileName;
1703     }
1704 
1705     /**
1706      * @deprecated will be removed without replacement
1707      */
1708     @Deprecated
1709     public void setDebug( boolean debug )
1710     {
1711     }
1712 
1713     /**
1714      * @deprecated will be removed without replacement
1715      */
1716     @Deprecated
1717     public boolean isMavenDebug()
1718     {
1719         return mavenDebug;
1720     }
1721 
1722     /**
1723      * For replacement please use:
1724      * <pre>
1725      *     verifier.addCliArgument( "-X" );
1726      * </pre>
1727      *
1728      * @deprecated will be removed without replacement.
1729      */
1730     @Deprecated
1731     public void setMavenDebug( boolean mavenDebug )
1732     {
1733         this.mavenDebug = mavenDebug;
1734     }
1735 
1736     public void setForkJvm( boolean forkJvm )
1737     {
1738         this.forkJvm = forkJvm;
1739     }
1740 
1741     public boolean isDebugJvm()
1742     {
1743         return debugJvm;
1744     }
1745 
1746     public void setDebugJvm( boolean debugJvm )
1747     {
1748         this.debugJvm = debugJvm;
1749     }
1750 
1751     public String getLocalRepoLayout()
1752     {
1753         return localRepoLayout;
1754     }
1755 
1756     public void setLocalRepoLayout( String localRepoLayout )
1757     {
1758         this.localRepoLayout = localRepoLayout;
1759     }
1760 
1761     public String getLocalRepository()
1762     {
1763         return localRepo;
1764     }
1765 }