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