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