1 | |
package org.apache.maven.shared.release.exec; |
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
import org.apache.commons.cli.CommandLine; |
23 | |
import org.apache.commons.cli.OptionBuilder; |
24 | |
import org.apache.commons.cli.Options; |
25 | |
import org.apache.commons.cli.PosixParser; |
26 | |
import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer; |
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.InvokerLogger; |
34 | |
import org.apache.maven.shared.invoker.MavenInvocationException; |
35 | |
import org.apache.maven.shared.release.ReleaseResult; |
36 | |
import org.apache.maven.shared.release.env.ReleaseEnvironment; |
37 | |
import org.codehaus.plexus.logging.Logger; |
38 | |
import org.codehaus.plexus.util.IOUtil; |
39 | |
import org.codehaus.plexus.util.StringUtils; |
40 | |
import org.codehaus.plexus.util.cli.CommandLineUtils; |
41 | |
|
42 | |
import java.io.File; |
43 | |
import java.io.FileWriter; |
44 | |
import java.io.IOException; |
45 | |
import java.util.ArrayList; |
46 | |
import java.util.List; |
47 | |
import java.util.Properties; |
48 | |
import java.util.StringTokenizer; |
49 | |
|
50 | |
|
51 | |
|
52 | |
|
53 | |
|
54 | |
|
55 | 82 | public class InvokerMavenExecutor |
56 | |
extends AbstractMavenExecutor |
57 | |
{ |
58 | |
|
59 | 2 | private static final Options OPTIONS = new Options(); |
60 | |
|
61 | |
private static final char SET_SYSTEM_PROPERTY = 'D'; |
62 | |
|
63 | |
private static final char OFFLINE = 'o'; |
64 | |
|
65 | |
private static final char REACTOR = 'r'; |
66 | |
|
67 | |
private static final char QUIET = 'q'; |
68 | |
|
69 | |
private static final char DEBUG = 'X'; |
70 | |
|
71 | |
private static final char ERRORS = 'e'; |
72 | |
|
73 | |
private static final char NON_RECURSIVE = 'N'; |
74 | |
|
75 | |
private static final char UPDATE_SNAPSHOTS = 'U'; |
76 | |
|
77 | |
private static final char ACTIVATE_PROFILES = 'P'; |
78 | |
|
79 | |
private static final String FORCE_PLUGIN_UPDATES = "cpu"; |
80 | |
|
81 | |
private static final String FORCE_PLUGIN_UPDATES2 = "up"; |
82 | |
|
83 | |
private static final String SUPPRESS_PLUGIN_UPDATES = "npu"; |
84 | |
|
85 | |
private static final String SUPPRESS_PLUGIN_REGISTRY = "npr"; |
86 | |
|
87 | |
private static final char CHECKSUM_FAILURE_POLICY = 'C'; |
88 | |
|
89 | |
private static final char CHECKSUM_WARNING_POLICY = 'c'; |
90 | |
|
91 | |
private static final char ALTERNATE_USER_SETTINGS = 's'; |
92 | |
|
93 | |
private static final String FAIL_FAST = "ff"; |
94 | |
|
95 | |
private static final String FAIL_AT_END = "fae"; |
96 | |
|
97 | |
private static final String FAIL_NEVER = "fn"; |
98 | |
|
99 | |
private static final String ALTERNATE_POM_FILE = "f"; |
100 | |
|
101 | |
static |
102 | |
{ |
103 | 2 | OPTIONS.addOption( |
104 | |
OptionBuilder.withLongOpt( "define" ).hasArg().withDescription( "Define a system property" ).create( |
105 | |
SET_SYSTEM_PROPERTY ) ); |
106 | |
|
107 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "offline" ).withDescription( "Work offline" ).create( OFFLINE ) ); |
108 | |
|
109 | 2 | OPTIONS.addOption( |
110 | |
OptionBuilder.withLongOpt( "quiet" ).withDescription( "Quiet output - only show errors" ).create( QUIET ) ); |
111 | |
|
112 | 2 | OPTIONS.addOption( |
113 | |
OptionBuilder.withLongOpt( "debug" ).withDescription( "Produce execution debug output" ).create( DEBUG ) ); |
114 | |
|
115 | 2 | OPTIONS.addOption( |
116 | |
OptionBuilder.withLongOpt( "errors" ).withDescription( "Produce execution error messages" ).create( |
117 | |
ERRORS ) ); |
118 | |
|
119 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "reactor" ).withDescription( |
120 | |
"Execute goals for project found in the reactor" ).create( REACTOR ) ); |
121 | |
|
122 | 2 | OPTIONS.addOption( |
123 | |
OptionBuilder.withLongOpt( "non-recursive" ).withDescription( "Do not recurse into sub-projects" ).create( |
124 | |
NON_RECURSIVE ) ); |
125 | |
|
126 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "update-snapshots" ).withDescription( |
127 | |
"Forces a check for updated releases and snapshots on remote repositories" ).create( UPDATE_SNAPSHOTS ) ); |
128 | |
|
129 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "activate-profiles" ).withDescription( |
130 | |
"Comma-delimited list of profiles to activate" ).hasArg().create( ACTIVATE_PROFILES ) ); |
131 | |
|
132 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "check-plugin-updates" ).withDescription( |
133 | |
"Force upToDate check for any relevant registered plugins" ).create( FORCE_PLUGIN_UPDATES ) ); |
134 | |
|
135 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "update-plugins" ).withDescription( |
136 | |
"Synonym for " + FORCE_PLUGIN_UPDATES ).create( FORCE_PLUGIN_UPDATES2 ) ); |
137 | |
|
138 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "no-plugin-updates" ).withDescription( |
139 | |
"Suppress upToDate check for any relevant registered plugins" ).create( SUPPRESS_PLUGIN_UPDATES ) ); |
140 | |
|
141 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "no-plugin-registry" ).withDescription( |
142 | |
"Don't use ~/.m2/plugin-registry.xml for plugin versions" ).create( SUPPRESS_PLUGIN_REGISTRY ) ); |
143 | |
|
144 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "strict-checksums" ).withDescription( |
145 | |
"Fail the build if checksums don't match" ).create( CHECKSUM_FAILURE_POLICY ) ); |
146 | |
|
147 | 2 | OPTIONS.addOption( |
148 | |
OptionBuilder.withLongOpt( "lax-checksums" ).withDescription( "Warn if checksums don't match" ).create( |
149 | |
CHECKSUM_WARNING_POLICY ) ); |
150 | |
|
151 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "settings" ).withDescription( |
152 | |
"Alternate path for the user settings file" ).hasArg().create( ALTERNATE_USER_SETTINGS ) ); |
153 | |
|
154 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "fail-fast" ).withDescription( |
155 | |
"Stop at first failure in reactorized builds" ).create( FAIL_FAST ) ); |
156 | |
|
157 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "fail-at-end" ).withDescription( |
158 | |
"Only fail the build afterwards; allow all non-impacted builds to continue" ).create( FAIL_AT_END ) ); |
159 | |
|
160 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "fail-never" ).withDescription( |
161 | |
"NEVER fail the build, regardless of project result" ).create( FAIL_NEVER ) ); |
162 | |
|
163 | 2 | OPTIONS.addOption( OptionBuilder.withLongOpt( "file" ).withDescription( |
164 | |
"Force the use of an alternate POM file." ).hasArg().create( ALTERNATE_POM_FILE ) ); |
165 | 2 | } |
166 | |
|
167 | |
|
168 | |
private void setupRequest( InvocationRequest req, |
169 | |
LoggerBridge bridge, |
170 | |
String additionalArguments ) |
171 | |
throws MavenExecutorException |
172 | |
{ |
173 | |
try |
174 | |
{ |
175 | 0 | String[] args = CommandLineUtils.translateCommandline( additionalArguments ); |
176 | 0 | CommandLine cli = new PosixParser().parse( OPTIONS, args ); |
177 | |
|
178 | 0 | if ( cli.hasOption( SET_SYSTEM_PROPERTY ) ) |
179 | |
{ |
180 | 0 | String[] properties = cli.getOptionValues( SET_SYSTEM_PROPERTY ); |
181 | 0 | Properties props = new Properties(); |
182 | 0 | for ( int i = 0; i < properties.length; i++ ) |
183 | |
{ |
184 | 0 | String property = properties[i]; |
185 | |
String name, value; |
186 | 0 | int sep = property.indexOf( "=" ); |
187 | 0 | if ( sep <= 0 ) |
188 | |
{ |
189 | 0 | name = property.trim(); |
190 | 0 | value = "true"; |
191 | |
} |
192 | |
else |
193 | |
{ |
194 | 0 | name = property.substring( 0, sep ).trim(); |
195 | 0 | value = property.substring( sep + 1 ).trim(); |
196 | |
} |
197 | 0 | props.setProperty( name, value ); |
198 | |
} |
199 | |
|
200 | 0 | req.setProperties( props ); |
201 | |
} |
202 | |
|
203 | 0 | if ( cli.hasOption( OFFLINE ) ) |
204 | |
{ |
205 | 0 | req.setOffline( true ); |
206 | |
} |
207 | |
|
208 | 0 | if ( cli.hasOption( QUIET ) ) |
209 | |
{ |
210 | |
|
211 | 0 | req.setDebug( false ); |
212 | |
} |
213 | 0 | else if ( cli.hasOption( DEBUG ) ) |
214 | |
{ |
215 | 0 | req.setDebug( true ); |
216 | |
} |
217 | 0 | else if ( cli.hasOption( ERRORS ) ) |
218 | |
{ |
219 | 0 | req.setShowErrors( true ); |
220 | |
} |
221 | |
|
222 | 0 | if ( cli.hasOption( REACTOR ) ) |
223 | |
{ |
224 | 0 | req.setRecursive( true ); |
225 | |
} |
226 | 0 | else if ( cli.hasOption( NON_RECURSIVE ) ) |
227 | |
{ |
228 | 0 | req.setRecursive( false ); |
229 | |
} |
230 | |
|
231 | 0 | if ( cli.hasOption( UPDATE_SNAPSHOTS ) ) |
232 | |
{ |
233 | 0 | req.setUpdateSnapshots( true ); |
234 | |
} |
235 | |
|
236 | 0 | if ( cli.hasOption( ACTIVATE_PROFILES ) ) |
237 | |
{ |
238 | 0 | String[] profiles = cli.getOptionValues( ACTIVATE_PROFILES ); |
239 | 0 | List<String> activatedProfiles = new ArrayList<String>(); |
240 | 0 | List<String> deactivatedProfiles = new ArrayList<String>(); |
241 | |
|
242 | 0 | if ( profiles != null ) |
243 | |
{ |
244 | 0 | for ( int i = 0; i < profiles.length; ++i ) |
245 | |
{ |
246 | 0 | StringTokenizer profileTokens = new StringTokenizer( profiles[i], "," ); |
247 | |
|
248 | 0 | while ( profileTokens.hasMoreTokens() ) |
249 | |
{ |
250 | 0 | String profileAction = profileTokens.nextToken().trim(); |
251 | |
|
252 | 0 | if ( profileAction.startsWith( "-" ) || profileAction.startsWith( "!" ) ) |
253 | |
{ |
254 | 0 | deactivatedProfiles.add( profileAction.substring( 1 ) ); |
255 | |
} |
256 | 0 | else if ( profileAction.startsWith( "+" ) ) |
257 | |
{ |
258 | 0 | activatedProfiles.add( profileAction.substring( 1 ) ); |
259 | |
} |
260 | |
else |
261 | |
{ |
262 | 0 | activatedProfiles.add( profileAction ); |
263 | |
} |
264 | 0 | } |
265 | |
} |
266 | |
} |
267 | |
|
268 | 0 | if ( !deactivatedProfiles.isEmpty() ) |
269 | |
{ |
270 | 0 | getLogger().warn( "Explicit profile deactivation is not yet supported. " |
271 | |
+ "The following profiles will NOT be deactivated: " + StringUtils.join( |
272 | |
deactivatedProfiles.iterator(), ", " ) ); |
273 | |
} |
274 | |
|
275 | 0 | if ( !activatedProfiles.isEmpty() ) |
276 | |
{ |
277 | 0 | req.setProfiles( activatedProfiles ); |
278 | |
} |
279 | |
} |
280 | |
|
281 | 0 | if ( cli.hasOption( FORCE_PLUGIN_UPDATES ) || cli.hasOption( FORCE_PLUGIN_UPDATES2 ) ) |
282 | |
{ |
283 | 0 | getLogger().warn( "Forcing plugin updates is not supported currently." ); |
284 | |
} |
285 | 0 | else if ( cli.hasOption( SUPPRESS_PLUGIN_UPDATES ) ) |
286 | |
{ |
287 | 0 | req.setNonPluginUpdates( true ); |
288 | |
} |
289 | |
|
290 | 0 | if ( cli.hasOption( SUPPRESS_PLUGIN_REGISTRY ) ) |
291 | |
{ |
292 | 0 | getLogger().warn( "Explicit suppression of the plugin registry is not supported currently." ); |
293 | |
} |
294 | |
|
295 | 0 | if ( cli.hasOption( CHECKSUM_FAILURE_POLICY ) ) |
296 | |
{ |
297 | 0 | req.setGlobalChecksumPolicy( InvocationRequest.CHECKSUM_POLICY_FAIL ); |
298 | |
} |
299 | 0 | else if ( cli.hasOption( CHECKSUM_WARNING_POLICY ) ) |
300 | |
{ |
301 | 0 | req.setGlobalChecksumPolicy( InvocationRequest.CHECKSUM_POLICY_WARN ); |
302 | |
} |
303 | |
|
304 | 0 | if ( cli.hasOption( ALTERNATE_USER_SETTINGS ) ) |
305 | |
{ |
306 | 0 | req.setUserSettingsFile( new File( cli.getOptionValue( ALTERNATE_USER_SETTINGS ) ) ); |
307 | |
} |
308 | |
|
309 | 0 | if ( cli.hasOption( FAIL_AT_END ) ) |
310 | |
{ |
311 | 0 | req.setFailureBehavior( InvocationRequest.REACTOR_FAIL_AT_END ); |
312 | |
} |
313 | 0 | else if ( cli.hasOption( FAIL_FAST ) ) |
314 | |
{ |
315 | 0 | req.setFailureBehavior( InvocationRequest.REACTOR_FAIL_FAST ); |
316 | |
} |
317 | 0 | if ( cli.hasOption( FAIL_NEVER ) ) |
318 | |
{ |
319 | 0 | req.setFailureBehavior( InvocationRequest.REACTOR_FAIL_NEVER ); |
320 | |
} |
321 | 0 | if ( cli.hasOption( ALTERNATE_POM_FILE ) ) |
322 | |
{ |
323 | 0 | if ( req.getPomFileName() != null ) |
324 | |
{ |
325 | 0 | getLogger().info( "pomFileName is already set, ignoring the -f argument" ); |
326 | |
} |
327 | |
else |
328 | |
{ |
329 | 0 | req.setPomFileName( cli.getOptionValue( ALTERNATE_POM_FILE ) ); |
330 | |
} |
331 | |
} |
332 | |
} |
333 | 0 | catch ( Exception e ) |
334 | |
{ |
335 | 0 | throw new MavenExecutorException( "Failed to re-parse additional arguments for Maven invocation.", e ); |
336 | 0 | } |
337 | 0 | } |
338 | |
|
339 | |
@Override |
340 | |
public void executeGoals( File workingDirectory, List<String> goals, ReleaseEnvironment releaseEnvironment, |
341 | |
boolean interactive, String additionalArguments, String pomFileName, |
342 | |
ReleaseResult result ) |
343 | |
throws MavenExecutorException |
344 | |
{ |
345 | 0 | Handler handler = new Handler( getLogger() ); |
346 | 0 | LoggerBridge bridge = new LoggerBridge( getLogger() ); |
347 | |
|
348 | 0 | Invoker invoker = |
349 | |
new DefaultInvoker().setMavenHome( releaseEnvironment.getMavenHome() ).setLogger( bridge ).setOutputHandler( |
350 | |
handler ).setErrorHandler( handler ); |
351 | |
|
352 | 0 | InvocationRequest req = |
353 | |
new DefaultInvocationRequest().setDebug( getLogger().isDebugEnabled() ).setBaseDirectory( |
354 | |
workingDirectory ).setInteractive( interactive ); |
355 | |
|
356 | 0 | if ( pomFileName != null ) |
357 | |
{ |
358 | 0 | req.setPomFileName( pomFileName ); |
359 | |
} |
360 | |
|
361 | 0 | File settingsFile = null; |
362 | 0 | if ( releaseEnvironment.getSettings() != null ) |
363 | |
{ |
364 | |
|
365 | |
try |
366 | |
{ |
367 | 0 | settingsFile = File.createTempFile( "release-settings", ".xml" ); |
368 | 0 | SettingsXpp3Writer writer = new SettingsXpp3Writer(); |
369 | 0 | FileWriter fileWriter = null; |
370 | |
try |
371 | |
{ |
372 | 0 | fileWriter = new FileWriter( settingsFile ); |
373 | 0 | writer.write( fileWriter, releaseEnvironment.getSettings() ); |
374 | |
} |
375 | |
finally |
376 | |
{ |
377 | 0 | IOUtil.close( fileWriter ); |
378 | 0 | } |
379 | 0 | req.setUserSettingsFile( settingsFile ); |
380 | |
} |
381 | 0 | catch ( IOException e ) |
382 | |
{ |
383 | 0 | throw new MavenExecutorException( "Could not create temporary file for release settings.xml", e ); |
384 | 0 | } |
385 | |
} |
386 | |
try |
387 | |
{ |
388 | 0 | File localRepoDir = releaseEnvironment.getLocalRepositoryDirectory(); |
389 | 0 | if ( localRepoDir != null ) |
390 | |
{ |
391 | 0 | req.setLocalRepositoryDirectory( localRepoDir ); |
392 | |
} |
393 | |
|
394 | 0 | setupRequest( req, bridge, additionalArguments ); |
395 | |
|
396 | 0 | req.setGoals( goals ); |
397 | |
|
398 | |
try |
399 | |
{ |
400 | 0 | InvocationResult invocationResult = invoker.execute( req ); |
401 | |
|
402 | 0 | if ( invocationResult.getExecutionException() != null ) |
403 | |
{ |
404 | 0 | throw new MavenExecutorException( "Error executing Maven.", |
405 | |
invocationResult.getExecutionException() ); |
406 | |
} |
407 | 0 | if ( invocationResult.getExitCode() != 0 ) |
408 | |
{ |
409 | 0 | throw new MavenExecutorException( |
410 | |
"Maven execution failed, exit code: \'" + invocationResult.getExitCode() + "\'", |
411 | |
invocationResult.getExitCode(), "", "" ); |
412 | |
} |
413 | |
} |
414 | 0 | catch ( MavenInvocationException e ) |
415 | |
{ |
416 | 0 | throw new MavenExecutorException( "Failed to invoke Maven build.", e ); |
417 | 0 | } |
418 | |
} |
419 | |
finally |
420 | |
{ |
421 | 0 | if ( settingsFile != null && settingsFile.exists() && !settingsFile.delete() ) |
422 | |
{ |
423 | 0 | settingsFile.deleteOnExit(); |
424 | |
} |
425 | |
} |
426 | 0 | } |
427 | |
|
428 | |
private static final class Handler |
429 | |
implements InvocationOutputHandler |
430 | |
{ |
431 | |
private Logger logger; |
432 | |
|
433 | |
Handler( Logger logger ) |
434 | 0 | { |
435 | 0 | this.logger = logger; |
436 | 0 | } |
437 | |
|
438 | |
public void consumeLine( String line ) |
439 | |
{ |
440 | 0 | logger.info( line ); |
441 | 0 | } |
442 | |
} |
443 | |
|
444 | 82 | private static final class LoggerBridge |
445 | |
implements InvokerLogger |
446 | |
{ |
447 | |
|
448 | |
private Logger logger; |
449 | |
|
450 | |
LoggerBridge( Logger logger ) |
451 | 0 | { |
452 | 0 | this.logger = logger; |
453 | 0 | } |
454 | |
|
455 | |
public void debug( String message, Throwable error ) |
456 | |
{ |
457 | 0 | logger.debug( message, error ); |
458 | 0 | } |
459 | |
|
460 | |
public void debug( String message ) |
461 | |
{ |
462 | 0 | logger.debug( message ); |
463 | 0 | } |
464 | |
|
465 | |
public void error( String message, Throwable error ) |
466 | |
{ |
467 | 0 | logger.error( message, error ); |
468 | 0 | } |
469 | |
|
470 | |
public void error( String message ) |
471 | |
{ |
472 | 0 | logger.error( message ); |
473 | 0 | } |
474 | |
|
475 | |
public void fatalError( String message, Throwable error ) |
476 | |
{ |
477 | 0 | logger.fatalError( message, error ); |
478 | 0 | } |
479 | |
|
480 | |
public void fatalError( String message ) |
481 | |
{ |
482 | 0 | logger.fatalError( message ); |
483 | 0 | } |
484 | |
|
485 | |
public Logger getChildLogger( String message ) |
486 | |
{ |
487 | 0 | return logger.getChildLogger( message ); |
488 | |
} |
489 | |
|
490 | |
public String getName() |
491 | |
{ |
492 | 0 | return logger.getName(); |
493 | |
} |
494 | |
|
495 | |
public int getThreshold() |
496 | |
{ |
497 | 0 | return logger.getThreshold(); |
498 | |
} |
499 | |
|
500 | |
public void info( String message, Throwable error ) |
501 | |
{ |
502 | 0 | logger.info( message, error ); |
503 | 0 | } |
504 | |
|
505 | |
public void info( String message ) |
506 | |
{ |
507 | 0 | logger.info( message ); |
508 | 0 | } |
509 | |
|
510 | |
public boolean isDebugEnabled() |
511 | |
{ |
512 | 0 | return logger.isDebugEnabled(); |
513 | |
} |
514 | |
|
515 | |
public boolean isErrorEnabled() |
516 | |
{ |
517 | 0 | return logger.isErrorEnabled(); |
518 | |
} |
519 | |
|
520 | |
public boolean isFatalErrorEnabled() |
521 | |
{ |
522 | 0 | return logger.isFatalErrorEnabled(); |
523 | |
} |
524 | |
|
525 | |
public boolean isInfoEnabled() |
526 | |
{ |
527 | 0 | return logger.isInfoEnabled(); |
528 | |
} |
529 | |
|
530 | |
public boolean isWarnEnabled() |
531 | |
{ |
532 | 0 | return logger.isWarnEnabled(); |
533 | |
} |
534 | |
|
535 | |
public void setThreshold( int level ) |
536 | |
{ |
537 | |
|
538 | |
|
539 | |
|
540 | 0 | } |
541 | |
|
542 | |
public void warn( String message, Throwable error ) |
543 | |
{ |
544 | 0 | logger.warn( message, error ); |
545 | 0 | } |
546 | |
|
547 | |
public void warn( String message ) |
548 | |
{ |
549 | 0 | logger.warn( message ); |
550 | 0 | } |
551 | |
|
552 | |
} |
553 | |
|
554 | |
} |