Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BuildTool |
|
| 2.5;2,5 | ||||
BuildTool$LoggerHandler |
|
| 2.5;2,5 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
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 | 3 | public class BuildTool |
48 | implements Initializable, Disposable | |
49 | { | |
50 | 2 | 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 | 2 | InvocationRequest request = createBasicInvocationRequest( pom, properties, goals, buildLogFile ); |
71 | ||
72 | 2 | 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 | 2 | return mavenInvoker.execute( request ); |
90 | } | |
91 | catch ( MavenInvocationException e ) | |
92 | { | |
93 | throw new TestToolsException( "Error executing maven.", e ); | |
94 | } | |
95 | finally | |
96 | { | |
97 | 2 | 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 | 3 | if ( mavenInvoker == null ) |
112 | { | |
113 | 3 | mavenInvoker = new DefaultInvoker(); |
114 | ||
115 | 3 | if ( System.getProperty( "maven.home" ) == null ) |
116 | { | |
117 | 3 | Properties envars = CommandLineUtils.getSystemEnvVars(); |
118 | ||
119 | 3 | String mavenHome = envars.getProperty( "M2_HOME" ); |
120 | ||
121 | 3 | if ( mavenHome != null ) |
122 | { | |
123 | 3 | mavenInvoker.setMavenHome( new File( mavenHome ) ); |
124 | } | |
125 | } | |
126 | } | |
127 | 3 | } |
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 | 2 | InvocationOutputHandler outHandler = request.getOutputHandler( null ); |
138 | ||
139 | 2 | if ( outHandler != null && ( outHandler instanceof LoggerHandler ) ) |
140 | { | |
141 | 2 | ( (LoggerHandler) outHandler ).close(); |
142 | } | |
143 | ||
144 | 2 | InvocationOutputHandler errHandler = request.getErrorHandler( null ); |
145 | ||
146 | 2 | if ( errHandler != null && ( outHandler == null || errHandler != outHandler ) |
147 | && ( errHandler instanceof LoggerHandler ) ) | |
148 | { | |
149 | ( (LoggerHandler) errHandler ).close(); | |
150 | } | |
151 | 2 | } |
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 | 2 | InvocationRequest request = new DefaultInvocationRequest(); |
171 | ||
172 | 2 | request.setPomFile( pom ); |
173 | ||
174 | 2 | request.setGoals( goals ); |
175 | ||
176 | 2 | request.setProperties( properties ); |
177 | ||
178 | 2 | LoggerHandler handler = new LoggerHandler( buildLogFile ); |
179 | ||
180 | 2 | request.setOutputHandler( handler ); |
181 | 2 | request.setErrorHandler( handler ); |
182 | ||
183 | 2 | return request; |
184 | } | |
185 | ||
186 | 3 | private static final class LoggerHandler |
187 | implements InvocationOutputHandler | |
188 | { | |
189 | 1 | 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 | 2 | { |
197 | 2 | output = logFile; |
198 | 2 | } |
199 | ||
200 | public void consumeLine( String line ) | |
201 | { | |
202 | 2 | if ( writer == null ) |
203 | { | |
204 | try | |
205 | { | |
206 | 2 | writer = new FileWriter( output ); |
207 | } | |
208 | 2 | catch ( IOException e ) |
209 | { | |
210 | 2 | 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 | 2 | IOUtil.close( writer ); |
230 | 2 | } |
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 | 3 | startInvoker(); |
244 | } | |
245 | catch ( IOException e ) | |
246 | { | |
247 | throw new InitializationException( "Error detecting maven home.", e ); | |
248 | 3 | } |
249 | ||
250 | 3 | } |
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 | // TODO: When we switch to the embedder, use this to deallocate the MavenEmbedder, along | |
259 | // with the PlexusContainer and ClassRealm that it wraps. | |
260 | 3 | } |
261 | } |