001package org.apache.maven.scm.plugin; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.File; 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Map.Entry; 029import java.util.Objects; 030import java.util.Properties; 031 032import org.apache.maven.plugin.AbstractMojo; 033import org.apache.maven.plugin.MojoExecutionException; 034import org.apache.maven.plugins.annotations.Component; 035import org.apache.maven.plugins.annotations.Parameter; 036import org.apache.maven.scm.ScmBranch; 037import org.apache.maven.scm.ScmException; 038import org.apache.maven.scm.ScmFileSet; 039import org.apache.maven.scm.ScmResult; 040import org.apache.maven.scm.ScmRevision; 041import org.apache.maven.scm.ScmTag; 042import org.apache.maven.scm.ScmVersion; 043import org.apache.maven.scm.manager.ScmManager; 044import org.apache.maven.scm.provider.ScmProviderRepository; 045import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost; 046import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository; 047import org.apache.maven.scm.repository.ScmRepository; 048import org.apache.maven.scm.repository.ScmRepositoryException; 049import org.apache.maven.settings.Server; 050import org.apache.maven.settings.Settings; 051import org.apache.maven.shared.model.fileset.FileSet; 052import org.apache.maven.shared.model.fileset.util.FileSetManager; 053import org.codehaus.plexus.util.StringUtils; 054import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; 055import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException; 056 057/** 058 * @author <a href="evenisse@apache.org">Emmanuel Venisse</a> 059 * @author Olivier Lamy 060 */ 061public abstract class AbstractScmMojo 062 extends AbstractMojo 063{ 064 065 protected static final String VERSION_TYPE_BRANCH = "branch"; 066 067 protected static final String VERSION_TYPE_REVISION = "revision"; 068 069 protected static final String VERSION_TYPE_TAG = "tag"; 070 071 protected static final String[] VALID_VERSION_TYPES = { VERSION_TYPE_BRANCH, 072 VERSION_TYPE_REVISION, VERSION_TYPE_TAG }; 073 074 /** 075 * The SCM connection URL. 076 */ 077 @Parameter( property = "connectionUrl", defaultValue = "${project.scm.connection}" ) 078 private String connectionUrl; 079 080 /** 081 * The SCM connection URL for developers. 082 */ 083 @Parameter( property = "developerConnectionUrl", defaultValue = "${project.scm.developerConnection}" ) 084 private String developerConnectionUrl; 085 086 /** 087 * The type of connection to use (connection or developerConnection). 088 */ 089 @Parameter( property = "connectionType", defaultValue = "connection" ) 090 private String connectionType; 091 092 /** 093 * The working directory. 094 */ 095 @Parameter( property = "workingDirectory" ) 096 private File workingDirectory; 097 098 /** 099 * The user name (used by svn, starteam and perforce protocol). 100 */ 101 @Parameter( property = "username" ) 102 private String username; 103 104 /** 105 * The user password (used by svn, starteam and perforce protocol). 106 */ 107 @Parameter( property = "password" ) 108 private String password; 109 110 /** 111 * The private key (used by java svn). 112 */ 113 @Parameter( property = "privateKey" ) 114 private String privateKey; 115 116 /** 117 * The passphrase (used by java svn). 118 */ 119 @Parameter( property = "passphrase" ) 120 private String passphrase; 121 122 /** 123 * The url of tags base directory (used by svn protocol). It is not 124 * necessary to set it if you use the standard svn layout 125 * (branches/tags/trunk). 126 */ 127 @Parameter( property = "tagBase" ) 128 private String tagBase; 129 130 /** 131 * Comma separated list of includes file pattern. 132 */ 133 @Parameter( property = "includes" ) 134 private String includes; 135 136 /** 137 * Comma separated list of excludes file pattern. 138 */ 139 @Parameter( property = "excludes" ) 140 private String excludes; 141 142 @Component 143 private ScmManager manager; 144 145 /** 146 * When this plugin requires Maven 3.0 as minimum, this component can be removed and o.a.m.s.c.SettingsDecrypter be 147 * used instead. 148 */ 149 @Component( hint = "mng-4384" ) 150 private SecDispatcher secDispatcher; 151 152 /** 153 * The base directory. 154 */ 155 @Parameter( property = "basedir", required = true ) 156 private File basedir; 157 158 @Parameter( defaultValue = "${settings}", readonly = true ) 159 private Settings settings; 160 161 /** 162 * List of System properties to pass to the JUnit tests. 163 */ 164 @Parameter 165 private Properties systemProperties; 166 167 /** 168 * List of provider implementations. 169 */ 170 @Parameter 171 private Map<String, String> providerImplementations; 172 173 /** 174 * Should distributed changes be pushed to the central repository? 175 * For many distributed SCMs like Git, a change like a commit 176 * is only stored in your local copy of the repository. Pushing 177 * the change allows your to more easily share it with other users. 178 * 179 * @since 1.4 180 */ 181 @Parameter( property = "pushChanges", defaultValue = "true" ) 182 private boolean pushChanges; 183 184 /** 185 * A workItem for SCMs like RTC, TFS etc, that may require additional 186 * information to perform a pushChange operation. 187 * 188 * @since 1.9.5 189 */ 190 @Parameter( property = "workItem" ) 191 private String workItem; 192 193 /** {@inheritDoc} */ 194 public void execute() 195 throws MojoExecutionException 196 { 197 if ( systemProperties != null ) 198 { 199 // Add all system properties configured by the user 200 Iterator<Object> iter = systemProperties.keySet().iterator(); 201 202 while ( iter.hasNext() ) 203 { 204 String key = (String) iter.next(); 205 206 String value = systemProperties.getProperty( key ); 207 208 System.setProperty( key, value ); 209 } 210 } 211 212 if ( providerImplementations != null && !providerImplementations.isEmpty() ) 213 { 214 for ( Entry<String, String> entry : providerImplementations.entrySet() ) 215 { 216 String providerType = entry.getKey(); 217 String providerImplementation = entry.getValue(); 218 getLog().info( 219 "Change the default '" + providerType + "' provider implementation to '" 220 + providerImplementation + "'." ); 221 getScmManager().setScmProviderImplementation( providerType, providerImplementation ); 222 } 223 } 224 } 225 226 protected void setConnectionType( String connectionType ) 227 { 228 this.connectionType = connectionType; 229 } 230 231 public String getConnectionUrl() 232 { 233 boolean requireDeveloperConnection = !"connection".equals( connectionType.toLowerCase() ); 234 if ( StringUtils.isNotEmpty( connectionUrl ) && !requireDeveloperConnection ) 235 { 236 return connectionUrl; 237 } 238 else if ( StringUtils.isNotEmpty( developerConnectionUrl ) ) 239 { 240 return developerConnectionUrl; 241 } 242 if ( requireDeveloperConnection ) 243 { 244 throw new NullPointerException( "You need to define a developerConnectionUrl parameter" ); 245 } 246 else 247 { 248 throw new NullPointerException( "You need to define a connectionUrl parameter" ); 249 } 250 } 251 252 public void setConnectionUrl( String connectionUrl ) 253 { 254 this.connectionUrl = connectionUrl; 255 } 256 257 public File getWorkingDirectory() 258 { 259 if ( workingDirectory == null ) 260 { 261 return basedir; 262 } 263 264 return workingDirectory; 265 } 266 267 public File getBasedir() 268 { 269 return this.basedir; 270 } 271 272 public void setWorkingDirectory( File workingDirectory ) 273 { 274 this.workingDirectory = workingDirectory; 275 } 276 277 public ScmManager getScmManager() 278 { 279 return manager; 280 } 281 282 public ScmFileSet getFileSet() 283 throws IOException 284 { 285 if ( includes != null || excludes != null ) 286 { 287 return new ScmFileSet( getWorkingDirectory(), includes, excludes ); 288 } 289 else 290 { 291 return new ScmFileSet( getWorkingDirectory() ); 292 } 293 } 294 295 public ScmRepository getScmRepository() 296 throws ScmException 297 { 298 ScmRepository repository; 299 300 try 301 { 302 repository = getScmManager().makeScmRepository( getConnectionUrl() ); 303 304 ScmProviderRepository providerRepo = repository.getProviderRepository(); 305 306 providerRepo.setPushChanges( pushChanges ); 307 308 if ( !StringUtils.isEmpty( workItem ) ) 309 { 310 providerRepo.setWorkItem( workItem ); 311 } 312 313 if ( !StringUtils.isEmpty( username ) ) 314 { 315 providerRepo.setUser( username ); 316 } 317 318 if ( !StringUtils.isEmpty( password ) ) 319 { 320 providerRepo.setPassword( password ); 321 } 322 323 if ( repository.getProviderRepository() instanceof ScmProviderRepositoryWithHost ) 324 { 325 ScmProviderRepositoryWithHost repo = (ScmProviderRepositoryWithHost) repository.getProviderRepository(); 326 327 loadInfosFromSettings( repo ); 328 329 if ( !StringUtils.isEmpty( username ) ) 330 { 331 repo.setUser( username ); 332 } 333 334 if ( !StringUtils.isEmpty( password ) ) 335 { 336 repo.setPassword( password ); 337 } 338 339 if ( !StringUtils.isEmpty( privateKey ) ) 340 { 341 repo.setPrivateKey( privateKey ); 342 } 343 344 if ( !StringUtils.isEmpty( passphrase ) ) 345 { 346 repo.setPassphrase( passphrase ); 347 } 348 } 349 350 if ( !StringUtils.isEmpty( tagBase ) && repository.getProvider().equals( "svn" ) ) 351 { 352 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository(); 353 354 svnRepo.setTagBase( tagBase ); 355 } 356 } 357 catch ( ScmRepositoryException e ) 358 { 359 if ( !e.getValidationMessages().isEmpty() ) 360 { 361 for ( String message : e.getValidationMessages() ) 362 { 363 getLog().error( message ); 364 } 365 } 366 367 throw new ScmException( "Can't load the scm provider.", e ); 368 } 369 catch ( Exception e ) 370 { 371 throw new ScmException( "Can't load the scm provider.", e ); 372 } 373 374 return repository; 375 } 376 377 /** 378 * Load username password from settings if user has not set them in JVM properties 379 * 380 * @param repo not null 381 */ 382 private void loadInfosFromSettings( ScmProviderRepositoryWithHost repo ) 383 { 384 if ( username == null || password == null ) 385 { 386 String host = repo.getHost(); 387 388 int port = repo.getPort(); 389 390 if ( port > 0 ) 391 { 392 host += ":" + port; 393 } 394 395 Server server = this.settings.getServer( host ); 396 397 if ( server != null ) 398 { 399 if ( username == null ) 400 { 401 username = server.getUsername(); 402 } 403 404 if ( password == null ) 405 { 406 password = decrypt( server.getPassword(), host ); 407 } 408 409 if ( privateKey == null ) 410 { 411 privateKey = server.getPrivateKey(); 412 } 413 414 if ( passphrase == null ) 415 { 416 passphrase = decrypt( server.getPassphrase(), host ); 417 } 418 } 419 } 420 } 421 422 private String decrypt( String str, String server ) 423 { 424 try 425 { 426 return secDispatcher.decrypt( str ); 427 } 428 catch ( SecDispatcherException e ) 429 { 430 getLog().warn( "Failed to decrypt password/passphrase for server " + server + ", using auth token as is" ); 431 return str; 432 } 433 } 434 435 public void checkResult( ScmResult result ) 436 throws MojoExecutionException 437 { 438 if ( !result.isSuccess() ) 439 { 440 getLog().error( "Provider message:" ); 441 442 getLog().error( result.getProviderMessage() == null ? "" : result.getProviderMessage() ); 443 444 getLog().error( "Command output:" ); 445 446 getLog().error( result.getCommandOutput() == null ? "" : result.getCommandOutput() ); 447 448 throw new MojoExecutionException( 449 "Command failed: " + Objects.toString( result.getProviderMessage() ) ); 450 } 451 } 452 453 public String getIncludes() 454 { 455 return includes; 456 } 457 458 public void setIncludes( String includes ) 459 { 460 this.includes = includes; 461 } 462 463 public String getExcludes() 464 { 465 return excludes; 466 } 467 468 public void setExcludes( String excludes ) 469 { 470 this.excludes = excludes; 471 } 472 473 public ScmVersion getScmVersion( String versionType, String version ) 474 throws MojoExecutionException 475 { 476 if ( StringUtils.isEmpty( versionType ) && StringUtils.isNotEmpty( version ) ) 477 { 478 throw new MojoExecutionException( "You must specify the version type." ); 479 } 480 481 if ( StringUtils.isEmpty( version ) ) 482 { 483 return null; 484 } 485 486 if ( VERSION_TYPE_BRANCH.equals( versionType ) ) 487 { 488 return new ScmBranch( version ); 489 } 490 491 if ( VERSION_TYPE_TAG.equals( versionType ) ) 492 { 493 return new ScmTag( version ); 494 } 495 496 if ( VERSION_TYPE_REVISION.equals( versionType ) ) 497 { 498 return new ScmRevision( version ); 499 } 500 501 throw new MojoExecutionException( "Unknown '" + versionType + "' version type." ); 502 } 503 504 protected void handleExcludesIncludesAfterCheckoutAndExport( File checkoutDirectory ) 505 throws MojoExecutionException 506 { 507 List<String> includes = new ArrayList<String>(); 508 509 if ( ! StringUtils.isBlank( this.getIncludes() ) ) 510 { 511 String[] tokens = StringUtils.split( this.getIncludes(), "," ); 512 for ( int i = 0; i < tokens.length; ++i ) 513 { 514 includes.add( tokens[i] ); 515 } 516 } 517 518 List<String> excludes = new ArrayList<String>(); 519 520 if ( ! StringUtils.isBlank( this.getExcludes() ) ) 521 { 522 String[] tokens = StringUtils.split( this.getExcludes(), "," ); 523 for ( int i = 0; i < tokens.length; ++i ) 524 { 525 excludes.add( tokens[i] ); 526 } 527 } 528 529 if ( includes.isEmpty() && excludes.isEmpty() ) 530 { 531 return; 532 } 533 534 FileSetManager fileSetManager = new FileSetManager(); 535 536 FileSet fileset = new FileSet(); 537 fileset.setDirectory( checkoutDirectory.getAbsolutePath() ); 538 fileset.setIncludes( excludes ); // revert the order to do the delete 539 fileset.setExcludes( includes ); 540 fileset.setUseDefaultExcludes( false ); 541 542 try 543 { 544 fileSetManager.delete( fileset ); 545 } 546 catch ( IOException e ) 547 { 548 throw new MojoExecutionException( "Error found while cleaning up output directory base on " 549 + "excludes/includes configurations.", e ); 550 } 551 552 } 553}