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 /** {@inheritDoc} */ 174 public void execute() 175 throws MojoExecutionException 176 { 177 if ( systemProperties != null ) 178 { 179 // Add all system properties configured by the user 180 Iterator<Object> iter = systemProperties.keySet().iterator(); 181 182 while ( iter.hasNext() ) 183 { 184 String key = (String) iter.next(); 185 186 String value = systemProperties.getProperty( key ); 187 188 System.setProperty( key, value ); 189 } 190 } 191 192 if ( providerImplementations != null && !providerImplementations.isEmpty() ) 193 { 194 for ( Entry<String, String> entry : providerImplementations.entrySet() ) 195 { 196 String providerType = entry.getKey(); 197 String providerImplementation = entry.getValue(); 198 getLog().info( 199 "Change the default '" + providerType + "' provider implementation to '" 200 + providerImplementation + "'." ); 201 getScmManager().setScmProviderImplementation( providerType, providerImplementation ); 202 } 203 } 204 } 205 206 protected void setConnectionType( String connectionType ) 207 { 208 this.connectionType = connectionType; 209 } 210 211 public String getConnectionUrl() 212 { 213 boolean requireDeveloperConnection = !"connection".equals( connectionType.toLowerCase() ); 214 if ( StringUtils.isNotEmpty( connectionUrl ) && !requireDeveloperConnection ) 215 { 216 return connectionUrl; 217 } 218 else if ( StringUtils.isNotEmpty( developerConnectionUrl ) ) 219 { 220 return developerConnectionUrl; 221 } 222 if ( requireDeveloperConnection ) 223 { 224 throw new NullPointerException( "You need to define a developerConnectionUrl parameter" ); 225 } 226 else 227 { 228 throw new NullPointerException( "You need to define a connectionUrl parameter" ); 229 } 230 } 231 232 public void setConnectionUrl( String connectionUrl ) 233 { 234 this.connectionUrl = connectionUrl; 235 } 236 237 public File getWorkingDirectory() 238 { 239 if ( workingDirectory == null ) 240 { 241 return basedir; 242 } 243 244 return workingDirectory; 245 } 246 247 public File getBasedir() 248 { 249 return this.basedir; 250 } 251 252 public void setWorkingDirectory( File workingDirectory ) 253 { 254 this.workingDirectory = workingDirectory; 255 } 256 257 public ScmManager getScmManager() 258 { 259 return manager; 260 } 261 262 public ScmFileSet getFileSet() 263 throws IOException 264 { 265 if ( includes != null || excludes != null ) 266 { 267 return new ScmFileSet( getWorkingDirectory(), includes, excludes ); 268 } 269 else 270 { 271 return new ScmFileSet( getWorkingDirectory() ); 272 } 273 } 274 275 public ScmRepository getScmRepository() 276 throws ScmException 277 { 278 ScmRepository repository; 279 280 try 281 { 282 repository = getScmManager().makeScmRepository( getConnectionUrl() ); 283 284 ScmProviderRepository providerRepo = repository.getProviderRepository(); 285 286 providerRepo.setPushChanges( pushChanges ); 287 288 if ( !StringUtils.isEmpty( username ) ) 289 { 290 providerRepo.setUser( username ); 291 } 292 293 if ( !StringUtils.isEmpty( password ) ) 294 { 295 providerRepo.setPassword( password ); 296 } 297 298 if ( repository.getProviderRepository() instanceof ScmProviderRepositoryWithHost ) 299 { 300 ScmProviderRepositoryWithHost repo = (ScmProviderRepositoryWithHost) repository.getProviderRepository(); 301 302 loadInfosFromSettings( repo ); 303 304 if ( !StringUtils.isEmpty( username ) ) 305 { 306 repo.setUser( username ); 307 } 308 309 if ( !StringUtils.isEmpty( password ) ) 310 { 311 repo.setPassword( password ); 312 } 313 314 if ( !StringUtils.isEmpty( privateKey ) ) 315 { 316 repo.setPrivateKey( privateKey ); 317 } 318 319 if ( !StringUtils.isEmpty( passphrase ) ) 320 { 321 repo.setPassphrase( passphrase ); 322 } 323 } 324 325 if ( !StringUtils.isEmpty( tagBase ) && repository.getProvider().equals( "svn" ) ) 326 { 327 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository(); 328 329 svnRepo.setTagBase( tagBase ); 330 } 331 } 332 catch ( ScmRepositoryException e ) 333 { 334 if ( !e.getValidationMessages().isEmpty() ) 335 { 336 for ( String message : e.getValidationMessages() ) 337 { 338 getLog().error( message ); 339 } 340 } 341 342 throw new ScmException( "Can't load the scm provider.", e ); 343 } 344 catch ( Exception e ) 345 { 346 throw new ScmException( "Can't load the scm provider.", e ); 347 } 348 349 return repository; 350 } 351 352 /** 353 * Load username password from settings if user has not set them in JVM properties 354 * 355 * @param repo not null 356 */ 357 private void loadInfosFromSettings( ScmProviderRepositoryWithHost repo ) 358 { 359 if ( username == null || password == null ) 360 { 361 String host = repo.getHost(); 362 363 int port = repo.getPort(); 364 365 if ( port > 0 ) 366 { 367 host += ":" + port; 368 } 369 370 Server server = this.settings.getServer( host ); 371 372 if ( server != null ) 373 { 374 if ( username == null ) 375 { 376 username = server.getUsername(); 377 } 378 379 if ( password == null ) 380 { 381 password = decrypt( server.getPassword(), host ); 382 } 383 384 if ( privateKey == null ) 385 { 386 privateKey = server.getPrivateKey(); 387 } 388 389 if ( passphrase == null ) 390 { 391 passphrase = decrypt( server.getPassphrase(), host ); 392 } 393 } 394 } 395 } 396 397 private String decrypt( String str, String server ) 398 { 399 try 400 { 401 return secDispatcher.decrypt( str ); 402 } 403 catch ( SecDispatcherException e ) 404 { 405 getLog().warn( "Failed to decrypt password/passphrase for server " + server + ", using auth token as is" ); 406 return str; 407 } 408 } 409 410 public void checkResult( ScmResult result ) 411 throws MojoExecutionException 412 { 413 if ( !result.isSuccess() ) 414 { 415 getLog().error( "Provider message:" ); 416 417 getLog().error( result.getProviderMessage() == null ? "" : result.getProviderMessage() ); 418 419 getLog().error( "Command output:" ); 420 421 getLog().error( result.getCommandOutput() == null ? "" : result.getCommandOutput() ); 422 423 throw new MojoExecutionException( 424 "Command failed." + StringUtils.defaultString( result.getProviderMessage() ) ); 425 } 426 } 427 428 public String getIncludes() 429 { 430 return includes; 431 } 432 433 public void setIncludes( String includes ) 434 { 435 this.includes = includes; 436 } 437 438 public String getExcludes() 439 { 440 return excludes; 441 } 442 443 public void setExcludes( String excludes ) 444 { 445 this.excludes = excludes; 446 } 447 448 public ScmVersion getScmVersion( String versionType, String version ) 449 throws MojoExecutionException 450 { 451 if ( StringUtils.isEmpty( versionType ) && StringUtils.isNotEmpty( version ) ) 452 { 453 throw new MojoExecutionException( "You must specify the version type." ); 454 } 455 456 if ( StringUtils.isEmpty( version ) ) 457 { 458 return null; 459 } 460 461 if ( "branch".equals( versionType ) ) 462 { 463 return new ScmBranch( version ); 464 } 465 466 if ( "tag".equals( versionType ) ) 467 { 468 return new ScmTag( version ); 469 } 470 471 if ( "revision".equals( versionType ) ) 472 { 473 return new ScmRevision( version ); 474 } 475 476 throw new MojoExecutionException( "Unknown '" + versionType + "' version type." ); 477 } 478 479 protected void handleExcludesIncludesAfterCheckoutAndExport( File checkoutDirectory ) 480 throws MojoExecutionException 481 { 482 List<String> includes = new ArrayList<String>(); 483 484 if ( ! StringUtils.isBlank( this.getIncludes() ) ) 485 { 486 String[] tokens = StringUtils.split( this.getIncludes(), "," ); 487 for ( int i = 0; i < tokens.length; ++i ) 488 { 489 includes.add( tokens[i] ); 490 } 491 } 492 493 List<String> excludes = new ArrayList<String>(); 494 495 if ( ! StringUtils.isBlank( this.getExcludes() ) ) 496 { 497 String[] tokens = StringUtils.split( this.getExcludes(), "," ); 498 for ( int i = 0; i < tokens.length; ++i ) 499 { 500 excludes.add( tokens[i] ); 501 } 502 } 503 504 if ( includes.isEmpty() && excludes.isEmpty() ) 505 { 506 return; 507 } 508 509 FileSetManager fileSetManager = new FileSetManager(); 510 511 FileSet fileset = new FileSet(); 512 fileset.setDirectory( checkoutDirectory.getAbsolutePath() ); 513 fileset.setIncludes( excludes ); // revert the order to do the delete 514 fileset.setExcludes( includes ); 515 fileset.setUseDefaultExcludes( false ); 516 517 try 518 { 519 fileSetManager.delete( fileset ); 520 } 521 catch ( IOException e ) 522 { 523 throw new MojoExecutionException( "Error found while cleaning up output directory base on " 524 + "excludes/includes configurations.", e ); 525 } 526 527 } 528}