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.update.UpdateScmResult; 046import org.apache.maven.scm.provider.AbstractScmProvider; 047import org.apache.maven.scm.provider.ScmProviderRepository; 048import org.apache.maven.scm.provider.svn.command.SvnCommand; 049import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository; 050import org.apache.maven.scm.provider.svn.util.SvnUtil; 051import org.apache.maven.scm.repository.ScmRepositoryException; 052import org.apache.maven.scm.repository.UnknownRepositoryStructure; 053import org.codehaus.plexus.util.StringUtils; 054 055/** 056 * SCM Provider for Subversion 057 * 058 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a> 059 * 060 */ 061public abstract class AbstractSvnScmProvider 062 extends AbstractScmProvider 063{ 064 // ---------------------------------------------------------------------- 065 // 066 // ---------------------------------------------------------------------- 067 068 private static class ScmUrlParserResult 069 { 070 private List<String> messages = new ArrayList<String>(); 071 072 private ScmProviderRepository repository; 073 } 074 075 private static final String CHECK_WORKING_DIRECTORY_URL = "scmCheckWorkingDirectoryUrl"; 076 077 // ---------------------------------------------------------------------- 078 // ScmProvider Implementation 079 // ---------------------------------------------------------------------- 080 081 /** 082 * {@inheritDoc} 083 */ 084 public String getScmSpecificFilename() 085 { 086 return ".svn"; 087 } 088 089 /** 090 * {@inheritDoc} 091 */ 092 public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter ) 093 throws ScmRepositoryException 094 { 095 ScmUrlParserResult result = parseScmUrl( scmSpecificUrl ); 096 097 if ( checkWorkingDirectoryUrl() ) 098 { 099 getLogger().debug( "Checking svn info 'URL:' field matches current sources directory" ); 100 try 101 { 102 String workingDir = System.getProperty( "scmCheckWorkingDirectoryUrl.currentWorkingDirectory" ); 103 InfoScmResult info = 104 info( result.repository, new ScmFileSet( new File( workingDir ) ), new CommandParameters() ); 105 106 String url = findUrlInfoItem( info ); 107 String comparison = "'" + url + "' vs. '" + scmSpecificUrl + "'"; 108 getLogger().debug( "Comparing : " + comparison ); 109 if ( url != null && !url.equals( scmSpecificUrl ) ) 110 { 111 result.messages.add( "Scm url does not match the value returned by svn info (" + comparison + ")" ); 112 } 113 } 114 catch ( ScmException e ) 115 { 116 throw new ScmRepositoryException( "An error occurred while trying to svn info", e ); 117 } 118 } 119 if ( result.messages.size() > 0 ) 120 { 121 throw new ScmRepositoryException( "The scm url is invalid.", result.messages ); 122 } 123 124 125 return result.repository; 126 } 127 128 private boolean checkWorkingDirectoryUrl() 129 { 130 return Boolean.getBoolean( CHECK_WORKING_DIRECTORY_URL ); 131 } 132 133 private String findUrlInfoItem( InfoScmResult infoScmResult ) 134 { 135 for ( InfoItem infoItem : infoScmResult.getInfoItems() ) 136 { 137 if ( infoItem.getURL() != null ) 138 { 139 getLogger().debug( "URL found: " + infoItem.getURL() ); 140 return infoItem.getURL(); 141 } 142 } 143 getLogger().debug( "URL not found (command output=" + infoScmResult.getCommandOutput() + ")" ); 144 return null; 145 } 146 147 /** 148 * {@inheritDoc} 149 */ 150 public ScmProviderRepository makeProviderScmRepository( File path ) 151 throws ScmRepositoryException, UnknownRepositoryStructure 152 { 153 if ( path == null ) 154 { 155 throw new NullPointerException( "Path argument is null" ); 156 } 157 158 if ( !path.isDirectory() ) 159 { 160 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a valid directory." ); 161 } 162 163 if ( !new File( path, ".svn" ).exists() ) 164 { 165 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a svn checkout directory." ); 166 } 167 168 try 169 { 170 return makeProviderScmRepository( getRepositoryURL( path ), ':' ); 171 } 172 catch ( ScmException e ) 173 { 174 // XXX We should allow throwing of SCMException. 175 throw new ScmRepositoryException( "Error executing info command", e ); 176 } 177 } 178 179 protected abstract String getRepositoryURL( File path ) 180 throws ScmException; 181 182 /** 183 * {@inheritDoc} 184 */ 185 public List<String> validateScmUrl( String scmSpecificUrl, char delimiter ) 186 { 187 List<String> messages = new ArrayList<String>(); 188 try 189 { 190 makeProviderScmRepository( scmSpecificUrl, delimiter ); 191 } 192 catch ( ScmRepositoryException e ) 193 { 194 messages = e.getValidationMessages(); 195 } 196 return messages; 197 } 198 199 /** 200 * {@inheritDoc} 201 */ 202 public String getScmType() 203 { 204 return "svn"; 205 } 206 207 // ---------------------------------------------------------------------- 208 // 209 // ---------------------------------------------------------------------- 210 211 private ScmUrlParserResult parseScmUrl( String scmSpecificUrl ) 212 { 213 ScmUrlParserResult result = new ScmUrlParserResult(); 214 215 String url = scmSpecificUrl; 216 217 // ---------------------------------------------------------------------- 218 // Do some sanity checking of the SVN url 219 // ---------------------------------------------------------------------- 220 221 if ( url.startsWith( "file" ) ) 222 { 223 if ( !url.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 ( url.startsWith( "https" ) ) 231 { 232 if ( !url.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 ( url.startsWith( "http" ) ) 240 { 241 if ( !url.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 ( url.startsWith( "svn+" ) ) 250 { 251 if ( url.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 = url.substring( "svn+".length(), url.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 ( url.startsWith( "svn" ) ) 281 { 282 if ( !url.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( url + " url isn't a valid svn URL." ); 292 293 return result; 294 } 295 296 result.repository = new SvnScmProviderRepository( url ); 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 getUpdateCommand(); 417 418 /** 419 * {@inheritDoc} 420 */ 421 public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 422 throws ScmException 423 { 424 return (UpdateScmResult) executeCommand( getUpdateCommand(), repository, fileSet, parameters ); 425 } 426 427 protected ScmResult executeCommand( SvnCommand command, ScmProviderRepository repository, ScmFileSet fileSet, 428 CommandParameters parameters ) 429 throws ScmException 430 { 431 command.setLogger( getLogger() ); 432 433 return command.execute( repository, fileSet, parameters ); 434 } 435 436 protected abstract SvnCommand getListCommand(); 437 438 /** 439 * {@inheritDoc} 440 */ 441 public ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 442 throws ScmException 443 { 444 SvnCommand cmd = getListCommand(); 445 446 return (ListScmResult) executeCommand( cmd, repository, fileSet, parameters ); 447 } 448 449 protected abstract SvnCommand getInfoCommand(); 450 451 public InfoScmResult info( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 452 throws ScmException 453 { 454 SvnCommand cmd = getInfoCommand(); 455 456 return (InfoScmResult) executeCommand( cmd, repository, fileSet, parameters ); 457 } 458 459 /** 460 * {@inheritDoc} 461 */ 462 protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 463 throws ScmException 464 { 465 SvnCommand cmd = getBlameCommand(); 466 467 return (BlameScmResult) executeCommand( cmd, repository, fileSet, parameters ); 468 } 469 470 protected abstract SvnCommand getBlameCommand(); 471 472 /** 473 * {@inheritDoc} 474 */ 475 public MkdirScmResult mkdir( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 476 throws ScmException 477 { 478 SvnCommand cmd = getMkdirCommand(); 479 480 return (MkdirScmResult) executeCommand( cmd, repository, fileSet, parameters ); 481 } 482 483 protected abstract SvnCommand getMkdirCommand(); 484 485 /** 486 * @param repository 487 * @param parameters 488 * @return true if remote url exists 489 * @throws ScmException 490 * @since 1.8 491 */ 492 public abstract boolean remoteUrlExist( ScmProviderRepository repository, CommandParameters parameters ) 493 throws ScmException; 494}