| package org.apache.maven.shared.test.plugin; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.Properties; |
| |
| import org.apache.maven.shared.invoker.DefaultInvocationRequest; |
| import org.apache.maven.shared.invoker.DefaultInvoker; |
| import org.apache.maven.shared.invoker.InvocationOutputHandler; |
| import org.apache.maven.shared.invoker.InvocationRequest; |
| import org.apache.maven.shared.invoker.InvocationResult; |
| import org.apache.maven.shared.invoker.Invoker; |
| import org.apache.maven.shared.invoker.MavenInvocationException; |
| import org.codehaus.plexus.component.annotations.Component; |
| import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; |
| import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; |
| import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; |
| import org.codehaus.plexus.util.IOUtil; |
| import org.codehaus.plexus.util.cli.CommandLineUtils; |
| |
| /** |
| * Test-tool used to execute Maven builds in order to test plugin functionality. |
| * |
| * @author jdcasey |
| * @version $Id$ |
| */ |
| @Component( role = BuildTool.class ) |
| public class BuildTool |
| implements Initializable, Disposable |
| { |
| /** Plexus role */ |
| public static final String ROLE = BuildTool.class.getName(); |
| |
| private Invoker mavenInvoker; |
| |
| /** |
| * Build a standard InvocationRequest using the specified test-build POM, command-line properties, |
| * goals, and output logfile. Then, execute Maven using this standard request. Return the result |
| * of the invocation. |
| * |
| * @param pom The test-build POM |
| * @param properties command-line properties to fine-tune the test build, or test parameter |
| * extraction from CLI properties |
| * @param goals The list of goals and/or lifecycle phases to execute during this build |
| * @param buildLogFile The logfile used to capture build output |
| * @return The result of the Maven invocation, including exit value and any execution exceptions |
| * resulting from the Maven invocation. |
| * @throws TestToolsException if any |
| */ |
| public InvocationResult executeMaven( File pom, Properties properties, List<String> goals, File buildLogFile ) |
| throws TestToolsException |
| { |
| InvocationRequest request = createBasicInvocationRequest( pom, properties, goals, buildLogFile ); |
| |
| return executeMaven( request ); |
| } |
| |
| /** |
| * Execute a test build using a customized InvocationRequest. Normally, this request would be |
| * created using the <code>createBasicInvocationRequest</code> method in this class. |
| * |
| * @param request The customized InvocationRequest containing the configuration used to execute |
| * the current test build |
| * @return The result of the Maven invocation, containing exit value, along with any execution |
| * exceptions resulting from the [attempted] Maven invocation. |
| * @throws TestToolsException if any |
| */ |
| public InvocationResult executeMaven( InvocationRequest request ) |
| throws TestToolsException |
| { |
| try |
| { |
| return mavenInvoker.execute( request ); |
| } |
| catch ( MavenInvocationException e ) |
| { |
| throw new TestToolsException( "Error executing maven.", e ); |
| } |
| finally |
| { |
| closeHandlers( request ); |
| } |
| } |
| |
| /** |
| * Detect the location of the local Maven installation, and start up the MavenInvoker using that |
| * path. Detection uses the system property <code>maven.home</code>, and falls back to the shell |
| * environment variable <code>M2_HOME</code>. |
| * |
| * @throws IOException in case the shell environment variables cannot be read |
| */ |
| private void startInvoker() |
| throws IOException |
| { |
| if ( mavenInvoker == null ) |
| { |
| mavenInvoker = new DefaultInvoker(); |
| |
| if ( System.getProperty( "maven.home" ) == null ) |
| { |
| Properties envars = CommandLineUtils.getSystemEnvVars(); |
| |
| String mavenHome = envars.getProperty( "M2_HOME" ); |
| |
| if ( mavenHome != null ) |
| { |
| mavenInvoker.setMavenHome( new File( mavenHome ) ); |
| } |
| } |
| } |
| } |
| |
| /** |
| * If we're logging output to a log file using standard output handlers, make sure these are |
| * closed. |
| * |
| * @param request |
| */ |
| private void closeHandlers( InvocationRequest request ) |
| { |
| InvocationOutputHandler outHandler = request.getOutputHandler( null ); |
| |
| if ( outHandler != null && ( outHandler instanceof LoggerHandler ) ) |
| { |
| ( (LoggerHandler) outHandler ).close(); |
| } |
| |
| InvocationOutputHandler errHandler = request.getErrorHandler( null ); |
| |
| if ( errHandler != null && ( outHandler == null || errHandler != outHandler ) |
| && ( errHandler instanceof LoggerHandler ) ) |
| { |
| ( (LoggerHandler) errHandler ).close(); |
| } |
| } |
| |
| /** |
| * Construct a standardized InvocationRequest given the test-build POM, a set of CLI properties, |
| * a list of goals to execute, and the location of a log file to which build output should be |
| * directed. The resulting InvocationRequest can then be customized by the test class before |
| * being used to execute a test build. Both standard-out and standard-error will be directed |
| * to the specified log file. |
| * |
| * @param pom The POM for the test build |
| * @param properties The command-line properties for use in this test build |
| * @param goals The goals and/or lifecycle phases to execute during the test build |
| * @param buildLogFile Location to which build output should be logged |
| * @return The standardized InvocationRequest for the test build, ready for any necessary |
| * customizations. |
| */ |
| public InvocationRequest createBasicInvocationRequest( File pom, Properties properties, List<String> goals, |
| File buildLogFile ) |
| { |
| InvocationRequest request = new DefaultInvocationRequest(); |
| |
| request.setPomFile( pom ); |
| |
| request.setGoals( goals ); |
| |
| request.setProperties( properties ); |
| |
| LoggerHandler handler = new LoggerHandler( buildLogFile ); |
| |
| request.setOutputHandler( handler ); |
| request.setErrorHandler( handler ); |
| |
| return request; |
| } |
| |
| private static final class LoggerHandler |
| implements InvocationOutputHandler |
| { |
| private static final String LS = System.getProperty( "line.separator" ); |
| |
| private final File output; |
| |
| private FileWriter writer; |
| |
| LoggerHandler( File logFile ) |
| { |
| output = logFile; |
| } |
| |
| /** {@inheritDoc} */ |
| public void consumeLine( String line ) |
| { |
| if ( writer == null ) |
| { |
| try |
| { |
| output.getParentFile().mkdirs(); |
| writer = new FileWriter( output ); |
| } |
| catch ( IOException e ) |
| { |
| throw new IllegalStateException( "Failed to open build log: " + output + "\n\nError: " |
| + e.getMessage() ); |
| } |
| } |
| |
| try |
| { |
| writer.write( line + LS ); |
| writer.flush(); |
| } |
| catch ( IOException e ) |
| { |
| throw new IllegalStateException( "Failed to write to build log: " + output + " output:\n\n\'" + line |
| + "\'\n\nError: " + e.getMessage() ); |
| } |
| } |
| |
| void close() |
| { |
| IOUtil.close( writer ); |
| } |
| } |
| |
| /** |
| * Initialize this tool once it's been instantiated and composed, in order to start up the |
| * MavenInvoker instance. |
| * |
| * @throws InitializationException if any |
| */ |
| public void initialize() |
| throws InitializationException |
| { |
| try |
| { |
| startInvoker(); |
| } |
| catch ( IOException e ) |
| { |
| throw new InitializationException( "Error detecting maven home.", e ); |
| } |
| } |
| |
| /** |
| * Not currently used; when this API switches to use the Maven Embedder, it will be used to |
| * shutdown the embedder and its associated container, to free up JVM memory. |
| */ |
| public void dispose() |
| { |
| // TODO: When we switch to the embedder, use this to deallocate the MavenEmbedder, along |
| // with the PlexusContainer and ClassRealm that it wraps. |
| } |
| } |