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