Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BuildTool |
|
| 2.9;2.9 | ||||
BuildTool$LoggerHandler |
|
| 2.9;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 | } |