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 String url = scmSpecificUrl; 218 219 // ---------------------------------------------------------------------- 220 // Do some sanity checking of the SVN url 221 // ---------------------------------------------------------------------- 222 223 if ( url.startsWith( "file" ) ) 224 { 225 if ( !url.startsWith( "file://" ) ) 226 { 227 result.messages.add( "A svn 'file' url must be on the form 'file://[hostname]/'." ); 228 229 return result; 230 } 231 } 232 else if ( url.startsWith( "https" ) ) 233 { 234 if ( !url.startsWith( "https://" ) ) 235 { 236 result.messages.add( "A svn 'http' url must be on the form 'https://'." ); 237 238 return result; 239 } 240 } 241 else if ( url.startsWith( "http" ) ) 242 { 243 if ( !url.startsWith( "http://" ) ) 244 { 245 result.messages.add( "A svn 'http' url must be on the form 'http://'." ); 246 247 return result; 248 } 249 } 250 // Support of tunnels: svn+xxx with xxx defined in subversion conf file 251 else if ( url.startsWith( "svn+" ) ) 252 { 253 if ( url.indexOf( "://" ) < 0 ) 254 { 255 result.messages.add( "A svn 'svn+xxx' url must be on the form 'svn+xxx://'." ); 256 257 return result; 258 } 259 else 260 { 261 String tunnel = url.substring( "svn+".length(), url.indexOf( "://" ) ); 262 263 //ssh is always an allowed tunnel 264 if ( !"ssh".equals( tunnel ) ) 265 { 266 SvnConfigFileReader reader = new SvnConfigFileReader(); 267 if ( SvnUtil.getSettings().getConfigDirectory() != null ) 268 { 269 reader.setConfigDirectory( new File( SvnUtil.getSettings().getConfigDirectory() ) ); 270 } 271 272 if ( StringUtils.isEmpty( reader.getProperty( "tunnels", tunnel ) ) ) 273 { 274 result.messages.add( 275 "The tunnel '" + tunnel + "' isn't defined in your subversion configuration file." ); 276 277 return result; 278 } 279 } 280 } 281 } 282 else if ( url.startsWith( "svn" ) ) 283 { 284 if ( !url.startsWith( "svn://" ) ) 285 { 286 result.messages.add( "A svn 'svn' url must be on the form 'svn://'." ); 287 288 return result; 289 } 290 } 291 else 292 { 293 result.messages.add( url + " url isn't a valid svn URL." ); 294 295 return result; 296 } 297 298 result.repository = new SvnScmProviderRepository( url ); 299 300 return result; 301 } 302 303 protected abstract SvnCommand getAddCommand(); 304 305 /** 306 * {@inheritDoc} 307 */ 308 public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 309 throws ScmException 310 { 311 return (AddScmResult) executeCommand( getAddCommand(), repository, fileSet, parameters ); 312 } 313 314 protected abstract SvnCommand getBranchCommand(); 315 316 /** 317 * {@inheritDoc} 318 */ 319 protected BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet, 320 CommandParameters parameters ) 321 throws ScmException 322 { 323 return (BranchScmResult) executeCommand( getBranchCommand(), repository, fileSet, parameters ); 324 } 325 326 protected abstract SvnCommand getChangeLogCommand(); 327 328 /** 329 * {@inheritDoc} 330 */ 331 public ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet, 332 CommandParameters parameters ) 333 throws ScmException 334 { 335 return (ChangeLogScmResult) executeCommand( getChangeLogCommand(), repository, fileSet, parameters ); 336 } 337 338 protected abstract SvnCommand getCheckInCommand(); 339 340 /** 341 * {@inheritDoc} 342 */ 343 public CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet, 344 CommandParameters parameters ) 345 throws ScmException 346 { 347 return (CheckInScmResult) executeCommand( getCheckInCommand(), repository, fileSet, parameters ); 348 } 349 350 protected abstract SvnCommand getCheckOutCommand(); 351 352 /** 353 * {@inheritDoc} 354 */ 355 public CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet, 356 CommandParameters parameters ) 357 throws ScmException 358 { 359 return (CheckOutScmResult) executeCommand( getCheckOutCommand(), repository, fileSet, parameters ); 360 } 361 362 protected abstract SvnCommand getDiffCommand(); 363 364 /** 365 * {@inheritDoc} 366 */ 367 public DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 368 throws ScmException 369 { 370 return (DiffScmResult) executeCommand( getDiffCommand(), repository, fileSet, parameters ); 371 } 372 373 protected abstract SvnCommand getExportCommand(); 374 375 /** 376 * {@inheritDoc} 377 */ 378 protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet, 379 CommandParameters parameters ) 380 throws ScmException 381 { 382 return (ExportScmResult) executeCommand( getExportCommand(), repository, fileSet, parameters ); 383 } 384 385 protected abstract SvnCommand getRemoveCommand(); 386 387 /** 388 * {@inheritDoc} 389 */ 390 public RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 391 throws ScmException 392 { 393 return (RemoveScmResult) executeCommand( getRemoveCommand(), repository, fileSet, parameters ); 394 } 395 396 protected abstract SvnCommand getStatusCommand(); 397 398 /** 399 * {@inheritDoc} 400 */ 401 public StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 402 throws ScmException 403 { 404 return (StatusScmResult) executeCommand( getStatusCommand(), repository, fileSet, parameters ); 405 } 406 407 protected abstract SvnCommand getTagCommand(); 408 409 /** 410 * {@inheritDoc} 411 */ 412 public TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 413 throws ScmException 414 { 415 return (TagScmResult) executeCommand( getTagCommand(), repository, fileSet, parameters ); 416 } 417 418 protected abstract SvnCommand getUntagCommand(); 419 420 /** 421 * {@inheritDoc} 422 */ 423 @Override 424 public UntagScmResult untag( ScmRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 425 throws ScmException 426 { 427 return (UntagScmResult) executeCommand( getUntagCommand(), repository.getProviderRepository(), 428 fileSet, parameters ); 429 } 430 431 protected abstract SvnCommand getUpdateCommand(); 432 433 /** 434 * {@inheritDoc} 435 */ 436 public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 437 throws ScmException 438 { 439 return (UpdateScmResult) executeCommand( getUpdateCommand(), repository, fileSet, parameters ); 440 } 441 442 protected ScmResult executeCommand( SvnCommand command, ScmProviderRepository repository, ScmFileSet fileSet, 443 CommandParameters parameters ) 444 throws ScmException 445 { 446 command.setLogger( getLogger() ); 447 448 return command.execute( repository, fileSet, parameters ); 449 } 450 451 protected abstract SvnCommand getListCommand(); 452 453 /** 454 * {@inheritDoc} 455 */ 456 public ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 457 throws ScmException 458 { 459 SvnCommand cmd = getListCommand(); 460 461 return (ListScmResult) executeCommand( cmd, repository, fileSet, parameters ); 462 } 463 464 protected abstract SvnCommand getInfoCommand(); 465 466 public InfoScmResult info( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 467 throws ScmException 468 { 469 SvnCommand cmd = getInfoCommand(); 470 471 return (InfoScmResult) executeCommand( cmd, repository, fileSet, parameters ); 472 } 473 474 /** 475 * {@inheritDoc} 476 */ 477 protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 478 throws ScmException 479 { 480 SvnCommand cmd = getBlameCommand(); 481 482 return (BlameScmResult) executeCommand( cmd, repository, fileSet, parameters ); 483 } 484 485 protected abstract SvnCommand getBlameCommand(); 486 487 /** 488 * {@inheritDoc} 489 */ 490 public MkdirScmResult mkdir( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 491 throws ScmException 492 { 493 SvnCommand cmd = getMkdirCommand(); 494 495 return (MkdirScmResult) executeCommand( cmd, repository, fileSet, parameters ); 496 } 497 498 protected abstract SvnCommand getMkdirCommand(); 499 500 /** 501 * @param repository 502 * @param parameters 503 * @return true if remote url exists 504 * @throws ScmException 505 * @since 1.8 506 */ 507 public abstract boolean remoteUrlExist( ScmProviderRepository repository, CommandParameters parameters ) 508 throws ScmException; 509}