001package org.apache.maven.scm.provider.svn; 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.util.ArrayList; 024import java.util.List; 025 026import org.apache.maven.scm.CommandParameters; 027import org.apache.maven.scm.ScmException; 028import org.apache.maven.scm.ScmFileSet; 029import org.apache.maven.scm.ScmResult; 030import org.apache.maven.scm.command.add.AddScmResult; 031import org.apache.maven.scm.command.blame.BlameScmResult; 032import org.apache.maven.scm.command.branch.BranchScmResult; 033import org.apache.maven.scm.command.changelog.ChangeLogScmResult; 034import org.apache.maven.scm.command.checkin.CheckInScmResult; 035import org.apache.maven.scm.command.checkout.CheckOutScmResult; 036import org.apache.maven.scm.command.diff.DiffScmResult; 037import org.apache.maven.scm.command.export.ExportScmResult; 038import org.apache.maven.scm.command.info.InfoItem; 039import org.apache.maven.scm.command.info.InfoScmResult; 040import org.apache.maven.scm.command.list.ListScmResult; 041import org.apache.maven.scm.command.mkdir.MkdirScmResult; 042import org.apache.maven.scm.command.remove.RemoveScmResult; 043import org.apache.maven.scm.command.status.StatusScmResult; 044import org.apache.maven.scm.command.tag.TagScmResult; 045import org.apache.maven.scm.command.untag.UntagScmResult; 046import org.apache.maven.scm.command.update.UpdateScmResult; 047import org.apache.maven.scm.provider.AbstractScmProvider; 048import org.apache.maven.scm.provider.ScmProviderRepository; 049import org.apache.maven.scm.provider.svn.command.SvnCommand; 050import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository; 051import org.apache.maven.scm.provider.svn.util.SvnUtil; 052import org.apache.maven.scm.repository.ScmRepository; 053import org.apache.maven.scm.repository.ScmRepositoryException; 054import org.apache.maven.scm.repository.UnknownRepositoryStructure; 055import org.codehaus.plexus.util.StringUtils; 056 057/** 058 * SCM Provider for Subversion 059 * 060 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a> 061 * 062 */ 063public abstract class AbstractSvnScmProvider 064 extends AbstractScmProvider 065{ 066 // ---------------------------------------------------------------------- 067 // 068 // ---------------------------------------------------------------------- 069 070 private static class ScmUrlParserResult 071 { 072 private List<String> messages = new ArrayList<String>(); 073 074 private ScmProviderRepository repository; 075 } 076 077 public static final String CURRENT_WORKING_DIRECTORY = "scmCheckWorkingDirectoryUrl.currentWorkingDirectory"; 078 079 // ---------------------------------------------------------------------- 080 // ScmProvider Implementation 081 // ---------------------------------------------------------------------- 082 083 /** 084 * {@inheritDoc} 085 */ 086 public String getScmSpecificFilename() 087 { 088 return ".svn"; 089 } 090 091 /** 092 * {@inheritDoc} 093 */ 094 public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter ) 095 throws ScmRepositoryException 096 { 097 ScmUrlParserResult result = parseScmUrl( scmSpecificUrl ); 098 099 if ( checkCurrentWorkingDirectoryUrl() ) 100 { 101 getLogger().debug( "Checking svn info 'URL:' field matches current sources directory" ); 102 try 103 { 104 String workingDir = System.getProperty( CURRENT_WORKING_DIRECTORY ); 105 InfoScmResult info = 106 info( result.repository, new ScmFileSet( new File( workingDir ) ), new CommandParameters() ); 107 108 String url = findUrlInfoItem( info ); 109 String comparison = "'" + url + "' vs. '" + scmSpecificUrl + "'"; 110 getLogger().debug( "Comparing : " + comparison ); 111 if ( url != null && !url.equals( scmSpecificUrl ) ) 112 { 113 result.messages.add( "Scm url does not match the value returned by svn info (" + comparison + ")" ); 114 } 115 } 116 catch ( ScmException e ) 117 { 118 throw new ScmRepositoryException( "An error occurred while trying to svn info", e ); 119 } 120 } 121 if ( result.messages.size() > 0 ) 122 { 123 throw new ScmRepositoryException( "The scm url is invalid.", result.messages ); 124 } 125 126 127 return result.repository; 128 } 129 130 private boolean checkCurrentWorkingDirectoryUrl() 131 { 132 return StringUtils.isNotEmpty( System.getProperty( CURRENT_WORKING_DIRECTORY ) ); 133 } 134 135 private String findUrlInfoItem( InfoScmResult infoScmResult ) 136 { 137 for ( InfoItem infoItem : infoScmResult.getInfoItems() ) 138 { 139 if ( infoItem.getURL() != null ) 140 { 141 getLogger().debug( "URL found: " + infoItem.getURL() ); 142 return infoItem.getURL(); 143 } 144 } 145 getLogger().debug( "URL not found (command output=" + infoScmResult.getCommandOutput() + ")" ); 146 return null; 147 } 148 149 /** 150 * {@inheritDoc} 151 */ 152 public ScmProviderRepository makeProviderScmRepository( File path ) 153 throws ScmRepositoryException, UnknownRepositoryStructure 154 { 155 if ( path == null ) 156 { 157 throw new NullPointerException( "Path argument is null" ); 158 } 159 160 if ( !path.isDirectory() ) 161 { 162 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a valid directory." ); 163 } 164 165 if ( !new File( path, ".svn" ).exists() ) 166 { 167 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a svn checkout directory." ); 168 } 169 170 try 171 { 172 return makeProviderScmRepository( getRepositoryURL( path ), ':' ); 173 } 174 catch ( ScmException e ) 175 { 176 // XXX We should allow throwing of SCMException. 177 throw new ScmRepositoryException( "Error executing info command", e ); 178 } 179 } 180 181 protected abstract String getRepositoryURL( File path ) 182 throws ScmException; 183 184 /** 185 * {@inheritDoc} 186 */ 187 public List<String> validateScmUrl( String scmSpecificUrl, char delimiter ) 188 { 189 List<String> messages = new ArrayList<String>(); 190 try 191 { 192 makeProviderScmRepository( scmSpecificUrl, delimiter ); 193 } 194 catch ( ScmRepositoryException e ) 195 { 196 messages = e.getValidationMessages(); 197 } 198 return messages; 199 } 200 201 /** 202 * {@inheritDoc} 203 */ 204 public String getScmType() 205 { 206 return "svn"; 207 } 208 209 // ---------------------------------------------------------------------- 210 // 211 // ---------------------------------------------------------------------- 212 213 private ScmUrlParserResult parseScmUrl( String scmSpecificUrl ) 214 { 215 ScmUrlParserResult result = new ScmUrlParserResult(); 216 217 // ---------------------------------------------------------------------- 218 // Do some sanity checking of the SVN url 219 // ---------------------------------------------------------------------- 220 221 if ( scmSpecificUrl.startsWith( "file" ) ) 222 { 223 if ( !scmSpecificUrl.startsWith( "file://" ) ) 224 { 225 result.messages.add( "A svn 'file' url must be on the form 'file://[hostname]/'." ); 226 227 return result; 228 } 229 } 230 else if ( scmSpecificUrl.startsWith( "https" ) ) 231 { 232 if ( !scmSpecificUrl.startsWith( "https://" ) ) 233 { 234 result.messages.add( "A svn 'http' url must be on the form 'https://'." ); 235 236 return result; 237 } 238 } 239 else if ( scmSpecificUrl.startsWith( "http" ) ) 240 { 241 if ( !scmSpecificUrl.startsWith( "http://" ) ) 242 { 243 result.messages.add( "A svn 'http' url must be on the form 'http://'." ); 244 245 return result; 246 } 247 } 248 // Support of tunnels: svn+xxx with xxx defined in subversion conf file 249 else if ( scmSpecificUrl.startsWith( "svn+" ) ) 250 { 251 if ( scmSpecificUrl.indexOf( "://" ) < 0 ) 252 { 253 result.messages.add( "A svn 'svn+xxx' url must be on the form 'svn+xxx://'." ); 254 255 return result; 256 } 257 else 258 { 259 String tunnel = scmSpecificUrl.substring( "svn+".length(), scmSpecificUrl.indexOf( "://" ) ); 260 261 //ssh is always an allowed tunnel 262 if ( !"ssh".equals( tunnel ) ) 263 { 264 SvnConfigFileReader reader = new SvnConfigFileReader(); 265 if ( SvnUtil.getSettings().getConfigDirectory() != null ) 266 { 267 reader.setConfigDirectory( new File( SvnUtil.getSettings().getConfigDirectory() ) ); 268 } 269 270 if ( StringUtils.isEmpty( reader.getProperty( "tunnels", tunnel ) ) ) 271 { 272 result.messages.add( 273 "The tunnel '" + tunnel + "' isn't defined in your subversion configuration file." ); 274 275 return result; 276 } 277 } 278 } 279 } 280 else if ( scmSpecificUrl.startsWith( "svn" ) ) 281 { 282 if ( !scmSpecificUrl.startsWith( "svn://" ) ) 283 { 284 result.messages.add( "A svn 'svn' url must be on the form 'svn://'." ); 285 286 return result; 287 } 288 } 289 else 290 { 291 result.messages.add( scmSpecificUrl + " url isn't a valid svn URL." ); 292 293 return result; 294 } 295 296 result.repository = new SvnScmProviderRepository( scmSpecificUrl ); 297 298 return result; 299 } 300 301 protected abstract SvnCommand getAddCommand(); 302 303 /** 304 * {@inheritDoc} 305 */ 306 public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 307 throws ScmException 308 { 309 return (AddScmResult) executeCommand( getAddCommand(), repository, fileSet, parameters ); 310 } 311 312 protected abstract SvnCommand getBranchCommand(); 313 314 /** 315 * {@inheritDoc} 316 */ 317 protected BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet, 318 CommandParameters parameters ) 319 throws ScmException 320 { 321 return (BranchScmResult) executeCommand( getBranchCommand(), repository, fileSet, parameters ); 322 } 323 324 protected abstract SvnCommand getChangeLogCommand(); 325 326 /** 327 * {@inheritDoc} 328 */ 329 public ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet, 330 CommandParameters parameters ) 331 throws ScmException 332 { 333 return (ChangeLogScmResult) executeCommand( getChangeLogCommand(), repository, fileSet, parameters ); 334 } 335 336 protected abstract SvnCommand getCheckInCommand(); 337 338 /** 339 * {@inheritDoc} 340 */ 341 public CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet, 342 CommandParameters parameters ) 343 throws ScmException 344 { 345 return (CheckInScmResult) executeCommand( getCheckInCommand(), repository, fileSet, parameters ); 346 } 347 348 protected abstract SvnCommand getCheckOutCommand(); 349 350 /** 351 * {@inheritDoc} 352 */ 353 public CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet, 354 CommandParameters parameters ) 355 throws ScmException 356 { 357 return (CheckOutScmResult) executeCommand( getCheckOutCommand(), repository, fileSet, parameters ); 358 } 359 360 protected abstract SvnCommand getDiffCommand(); 361 362 /** 363 * {@inheritDoc} 364 */ 365 public DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 366 throws ScmException 367 { 368 return (DiffScmResult) executeCommand( getDiffCommand(), repository, fileSet, parameters ); 369 } 370 371 protected abstract SvnCommand getExportCommand(); 372 373 /** 374 * {@inheritDoc} 375 */ 376 protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet, 377 CommandParameters parameters ) 378 throws ScmException 379 { 380 return (ExportScmResult) executeCommand( getExportCommand(), repository, fileSet, parameters ); 381 } 382 383 protected abstract SvnCommand getRemoveCommand(); 384 385 /** 386 * {@inheritDoc} 387 */ 388 public RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 389 throws ScmException 390 { 391 return (RemoveScmResult) executeCommand( getRemoveCommand(), repository, fileSet, parameters ); 392 } 393 394 protected abstract SvnCommand getStatusCommand(); 395 396 /** 397 * {@inheritDoc} 398 */ 399 public StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 400 throws ScmException 401 { 402 return (StatusScmResult) executeCommand( getStatusCommand(), repository, fileSet, parameters ); 403 } 404 405 protected abstract SvnCommand getTagCommand(); 406 407 /** 408 * {@inheritDoc} 409 */ 410 public TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 411 throws ScmException 412 { 413 return (TagScmResult) executeCommand( getTagCommand(), repository, fileSet, parameters ); 414 } 415 416 protected abstract SvnCommand getUntagCommand(); 417 418 /** 419 * {@inheritDoc} 420 */ 421 @Override 422 public UntagScmResult untag( ScmRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 423 throws ScmException 424 { 425 return (UntagScmResult) executeCommand( getUntagCommand(), repository.getProviderRepository(), 426 fileSet, parameters ); 427 } 428 429 protected abstract SvnCommand getUpdateCommand(); 430 431 /** 432 * {@inheritDoc} 433 */ 434 public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 435 throws ScmException 436 { 437 return (UpdateScmResult) executeCommand( getUpdateCommand(), repository, fileSet, parameters ); 438 } 439 440 protected ScmResult executeCommand( SvnCommand command, ScmProviderRepository repository, ScmFileSet fileSet, 441 CommandParameters parameters ) 442 throws ScmException 443 { 444 command.setLogger( getLogger() ); 445 446 return command.execute( repository, fileSet, parameters ); 447 } 448 449 protected abstract SvnCommand getListCommand(); 450 451 /** 452 * {@inheritDoc} 453 */ 454 public ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 455 throws ScmException 456 { 457 SvnCommand cmd = getListCommand(); 458 459 return (ListScmResult) executeCommand( cmd, repository, fileSet, parameters ); 460 } 461 462 protected abstract SvnCommand getInfoCommand(); 463 464 public InfoScmResult info( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 465 throws ScmException 466 { 467 SvnCommand cmd = getInfoCommand(); 468 469 return (InfoScmResult) executeCommand( cmd, repository, fileSet, parameters ); 470 } 471 472 /** 473 * {@inheritDoc} 474 */ 475 protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 476 throws ScmException 477 { 478 SvnCommand cmd = getBlameCommand(); 479 480 return (BlameScmResult) executeCommand( cmd, repository, fileSet, parameters ); 481 } 482 483 protected abstract SvnCommand getBlameCommand(); 484 485 /** 486 * {@inheritDoc} 487 */ 488 public MkdirScmResult mkdir( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 489 throws ScmException 490 { 491 SvnCommand cmd = getMkdirCommand(); 492 493 return (MkdirScmResult) executeCommand( cmd, repository, fileSet, parameters ); 494 } 495 496 protected abstract SvnCommand getMkdirCommand(); 497 498 /** 499 * @param repository 500 * @param parameters 501 * @return true if remote url exists 502 * @throws ScmException 503 * @since 1.8 504 */ 505 public abstract boolean remoteUrlExist( ScmProviderRepository repository, CommandParameters parameters ) 506 throws ScmException; 507}