Coverage Report - org.apache.maven.shared.test.plugin.BuildTool
 
Classes in this File Line Coverage Branch Coverage Complexity
BuildTool
76%
29/38
38%
7/18
2.9
BuildTool$LoggerHandler
77%
14/18
100%
2/2
2.9
 
 1  
 package org.apache.maven.shared.test.plugin;
 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.FileWriter;
 24  
 import java.io.IOException;
 25  
 import java.util.List;
 26  
 import java.util.Properties;
 27  
 
 28  
 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
 29  
 import org.apache.maven.shared.invoker.DefaultInvoker;
 30  
 import org.apache.maven.shared.invoker.InvocationOutputHandler;
 31  
 import org.apache.maven.shared.invoker.InvocationRequest;
 32  
 import org.apache.maven.shared.invoker.InvocationResult;
 33  
 import org.apache.maven.shared.invoker.Invoker;
 34  
 import org.apache.maven.shared.invoker.MavenInvocationException;
 35  
 import org.codehaus.plexus.component.annotations.Component;
 36  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
 37  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
 38  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
 39  
 import org.codehaus.plexus.util.IOUtil;
 40  
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 41  
 
 42  
 /**
 43  
  * Test-tool used to execute Maven builds in order to test plugin functionality.
 44  
  *
 45  
  * @author jdcasey
 46  
  * @version $Id: BuildTool.java 1185951 2011-10-19 02:43:50Z ifedorenko $
 47  
  */
 48  
 @Component(role=BuildTool.class)
 49  6
 public class BuildTool
 50  
     implements Initializable, Disposable
 51  
 {
 52  
     /** Plexus role */
 53  2
     public static final String ROLE = BuildTool.class.getName();
 54  
 
 55  
     private Invoker mavenInvoker;
 56  
 
 57  
     /**
 58  
      * Build a standard InvocationRequest using the specified test-build POM, command-line properties,
 59  
      * goals, and output logfile. Then, execute Maven using this standard request. Return the result
 60  
      * of the invocation.
 61  
      *
 62  
      * @param pom The test-build POM
 63  
      * @param properties command-line properties to fine-tune the test build, or test parameter
 64  
      *   extraction from CLI properties
 65  
      * @param goals The list of goals and/or lifecycle phases to execute during this build
 66  
      * @param buildLogFile The logfile used to capture build output
 67  
      * @return The result of the Maven invocation, including exit value and any execution exceptions
 68  
      *   resulting from the Maven invocation.
 69  
      * @throws TestToolsException if any
 70  
      */
 71  
     public InvocationResult executeMaven( File pom, Properties properties, List goals, File buildLogFile )
 72  
         throws TestToolsException
 73  
     {
 74  4
         InvocationRequest request = createBasicInvocationRequest( pom, properties, goals, buildLogFile );
 75  
 
 76  4
         return executeMaven( request );
 77  
     }
 78  
 
 79  
     /**
 80  
      * Execute a test build using a customized InvocationRequest. Normally, this request would be
 81  
      * created using the <code>createBasicInvocationRequest</code> method in this class.
 82  
      *
 83  
      * @param request The customized InvocationRequest containing the configuration used to execute
 84  
      *   the current test build
 85  
      * @return The result of the Maven invocation, containing exit value, along with any execution
 86  
      *   exceptions resulting from the [attempted] Maven invocation.
 87  
      * @throws TestToolsException if any
 88  
      */
 89  
     public InvocationResult executeMaven( InvocationRequest request )
 90  
         throws TestToolsException
 91  
     {
 92  
         try
 93  
         {
 94  4
             return mavenInvoker.execute( request );
 95  
         }
 96  0
         catch ( MavenInvocationException e )
 97  
         {
 98  0
             throw new TestToolsException( "Error executing maven.", e );
 99  
         }
 100  
         finally
 101  
         {
 102  4
             closeHandlers( request );
 103  
         }
 104  
     }
 105  
 
 106  
     /**
 107  
      * Detect the location of the local Maven installation, and start up the MavenInvoker using that
 108  
      * path. Detection uses the system property <code>maven.home</code>, and falls back to the shell
 109  
      * environment variable <code>M2_HOME</code>.
 110  
      *
 111  
      * @throws IOException in case the shell environment variables cannot be read
 112  
      */
 113  
     private void startInvoker()
 114  
         throws IOException
 115  
     {
 116  6
         if ( mavenInvoker == null )
 117  
         {
 118  6
             mavenInvoker = new DefaultInvoker();
 119  
 
 120  6
             if ( System.getProperty( "maven.home" ) == null )
 121  
             {
 122  0
                 Properties envars = CommandLineUtils.getSystemEnvVars();
 123  
 
 124  0
                 String mavenHome = envars.getProperty( "M2_HOME" );
 125  
 
 126  0
                 if ( mavenHome != null )
 127  
                 {
 128  0
                     mavenInvoker.setMavenHome( new File( mavenHome ) );
 129  
                 }
 130  
             }
 131  
         }
 132  6
     }
 133  
 
 134  
     /**
 135  
      * If we're logging output to a logfile using standard output handlers, make sure these are
 136  
      * closed.
 137  
      *
 138  
      * @param request
 139  
      */
 140  
     private void closeHandlers( InvocationRequest request )
 141  
     {
 142  4
         InvocationOutputHandler outHandler = request.getOutputHandler( null );
 143  
 
 144  4
         if ( outHandler != null && ( outHandler instanceof LoggerHandler ) )
 145  
         {
 146  4
             ( (LoggerHandler) outHandler ).close();
 147  
         }
 148  
 
 149  4
         InvocationOutputHandler errHandler = request.getErrorHandler( null );
 150  
 
 151  4
         if ( errHandler != null && ( outHandler == null || errHandler != outHandler )
 152  
             && ( errHandler instanceof LoggerHandler ) )
 153  
         {
 154  0
             ( (LoggerHandler) errHandler ).close();
 155  
         }
 156  4
     }
 157  
 
 158  
     /**
 159  
      * Construct a standardized InvocationRequest given the test-build POM, a set of CLI properties,
 160  
      * a list of goals to execute, and the location of a log file to which build output should be
 161  
      * directed. The resulting InvocationRequest can then be customized by the test class before
 162  
      * being used to execute a test build. Both standard-out and standard-error will be directed
 163  
      * to the specified log file.
 164  
      *
 165  
      * @param pom The POM for the test build
 166  
      * @param properties The command-line properties for use in this test build
 167  
      * @param goals The goals and/or lifecycle phases to execute during the test build
 168  
      * @param buildLogFile Location to which build output should be logged
 169  
      * @return The standardized InvocationRequest for the test build, ready for any necessary
 170  
      *   customizations.
 171  
      */
 172  
     public InvocationRequest createBasicInvocationRequest( File pom, Properties properties, List goals,
 173  
                                                            File buildLogFile )
 174  
     {
 175  4
         InvocationRequest request = new DefaultInvocationRequest();
 176  
 
 177  4
         request.setPomFile( pom );
 178  
 
 179  4
         request.setGoals( goals );
 180  
 
 181  4
         request.setProperties( properties );
 182  
 
 183  4
         LoggerHandler handler = new LoggerHandler( buildLogFile );
 184  
 
 185  4
         request.setOutputHandler( handler );
 186  4
         request.setErrorHandler( handler );
 187  
 
 188  4
         return request;
 189  
     }
 190  
 
 191  6
     private static final class LoggerHandler
 192  
         implements InvocationOutputHandler
 193  
     {
 194  2
         private static final String LS = System.getProperty( "line.separator" );
 195  
 
 196  
         private final File output;
 197  
 
 198  
         private FileWriter writer;
 199  
 
 200  
         LoggerHandler( File logFile )
 201  4
         {
 202  4
             output = logFile;
 203  4
         }
 204  
 
 205  
         /** {@inheritDoc} */
 206  
         public void consumeLine( String line )
 207  
         {
 208  236
             if ( writer == null )
 209  
             {
 210  
                 try
 211  
                 {
 212  4
                     output.getParentFile().mkdirs();
 213  4
                     writer = new FileWriter( output );
 214  
                 }
 215  0
                 catch ( IOException e )
 216  
                 {
 217  0
                     throw new IllegalStateException( "Failed to open build log: " + output + "\n\nError: "
 218  
                         + e.getMessage() );
 219  4
                 }
 220  
             }
 221  
 
 222  
             try
 223  
             {
 224  236
                 writer.write( line + LS );
 225  236
                 writer.flush();
 226  
             }
 227  0
             catch ( IOException e )
 228  
             {
 229  0
                 throw new IllegalStateException( "Failed to write to build log: " + output + " output:\n\n\'" + line
 230  
                     + "\'\n\nError: " + e.getMessage() );
 231  236
             }
 232  236
         }
 233  
 
 234  
         void close()
 235  
         {
 236  4
             IOUtil.close( writer );
 237  4
         }
 238  
     }
 239  
 
 240  
     /**
 241  
      * Initialize this tool once it's been instantiated and composed, in order to start up the
 242  
      * MavenInvoker instance.
 243  
      *
 244  
      * @throws InitializationException if any
 245  
      */
 246  
     public void initialize()
 247  
         throws InitializationException
 248  
     {
 249  
         try
 250  
         {
 251  6
             startInvoker();
 252  
         }
 253  0
         catch ( IOException e )
 254  
         {
 255  0
             throw new InitializationException( "Error detecting maven home.", e );
 256  6
         }
 257  6
     }
 258  
 
 259  
     /**
 260  
      * Not currently used; when this API switches to use the Maven Embedder, it will be used to
 261  
      * shutdown the embedder and its associated container, to free up JVM memory.
 262  
      */
 263  
     public void dispose()
 264  
     {
 265  
         // TODO: When we switch to the embedder, use this to deallocate the MavenEmbedder, along
 266  
         // with the PlexusContainer and ClassRealm that it wraps.
 267  6
     }
 268  
 }