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 InfoScmResult info = 103 info( result.repository, new ScmFileSet( new File( "." ) ), new CommandParameters() ); 104 String url = findUrlInfoItem( info ); 105 if ( url != null && !url.equals( scmSpecificUrl ) ) 106 { 107 result.messages.add( "The scm url does not match the value returned by svn info" ); 108 } 109 } 110 catch ( ScmException e ) 111 { 112 throw new ScmRepositoryException( "An error occurred while trying to svn info", e ); 113 } 114 } 115 if ( result.messages.size() > 0 ) 116 { 117 throw new ScmRepositoryException( "The scm url is invalid.", result.messages ); 118 } 119 120 121 return result.repository; 122 } 123 124 private boolean checkWorkingDirectoryUrl() 125 { 126 return Boolean.getBoolean( CHECK_WORKING_DIRECTORY_URL ); 127 } 128 129 private String findUrlInfoItem( InfoScmResult infoScmResult ) 130 { 131 for ( InfoItem infoItem : infoScmResult.getInfoItems() ) 132 { 133 if ( infoItem.getURL() != null ) 134 { 135 getLogger().debug( "URL found: " + infoItem.getURL() ); 136 return infoItem.getURL(); 137 } 138 } 139 return null; 140 } 141 142 /** 143 * {@inheritDoc} 144 */ 145 public ScmProviderRepository makeProviderScmRepository( File path ) 146 throws ScmRepositoryException, UnknownRepositoryStructure 147 { 148 if ( path == null ) 149 { 150 throw new NullPointerException( "Path argument is null" ); 151 } 152 153 if ( !path.isDirectory() ) 154 { 155 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a valid directory." ); 156 } 157 158 if ( !new File( path, ".svn" ).exists() ) 159 { 160 throw new ScmRepositoryException( path.getAbsolutePath() + " isn't a svn checkout directory." ); 161 } 162 163 try 164 { 165 return makeProviderScmRepository( getRepositoryURL( path ), ':' ); 166 } 167 catch ( ScmException e ) 168 { 169 // XXX We should allow throwing of SCMException. 170 throw new ScmRepositoryException( "Error executing info command", e ); 171 } 172 } 173 174 protected abstract String getRepositoryURL( File path ) 175 throws ScmException; 176 177 /** 178 * {@inheritDoc} 179 */ 180 public List<String> validateScmUrl( String scmSpecificUrl, char delimiter ) 181 { 182 List<String> messages = new ArrayList<String>(); 183 try 184 { 185 makeProviderScmRepository( scmSpecificUrl, delimiter ); 186 } 187 catch ( ScmRepositoryException e ) 188 { 189 messages = e.getValidationMessages(); 190 } 191 return messages; 192 } 193 194 /** 195 * {@inheritDoc} 196 */ 197 public String getScmType() 198 { 199 return "svn"; 200 } 201 202 // ---------------------------------------------------------------------- 203 // 204 // ---------------------------------------------------------------------- 205 206 private ScmUrlParserResult parseScmUrl( String scmSpecificUrl ) 207 { 208 ScmUrlParserResult result = new ScmUrlParserResult(); 209 210 String url = scmSpecificUrl; 211 212 // ---------------------------------------------------------------------- 213 // Do some sanity checking of the SVN url 214 // ---------------------------------------------------------------------- 215 216 if ( url.startsWith( "file" ) ) 217 { 218 if ( !url.startsWith( "file://" ) ) 219 { 220 result.messages.add( "A svn 'file' url must be on the form 'file://[hostname]/'." ); 221 222 return result; 223 } 224 } 225 else if ( url.startsWith( "https" ) ) 226 { 227 if ( !url.startsWith( "https://" ) ) 228 { 229 result.messages.add( "A svn 'http' url must be on the form 'https://'." ); 230 231 return result; 232 } 233 } 234 else if ( url.startsWith( "http" ) ) 235 { 236 if ( !url.startsWith( "http://" ) ) 237 { 238 result.messages.add( "A svn 'http' url must be on the form 'http://'." ); 239 240 return result; 241 } 242 } 243 // Support of tunnels: svn+xxx with xxx defined in subversion conf file 244 else if ( url.startsWith( "svn+" ) ) 245 { 246 if ( url.indexOf( "://" ) < 0 ) 247 { 248 result.messages.add( "A svn 'svn+xxx' url must be on the form 'svn+xxx://'." ); 249 250 return result; 251 } 252 else 253 { 254 String tunnel = url.substring( "svn+".length(), url.indexOf( "://" ) ); 255 256 //ssh is always an allowed tunnel 257 if ( !"ssh".equals( tunnel ) ) 258 { 259 SvnConfigFileReader reader = new SvnConfigFileReader(); 260 if ( SvnUtil.getSettings().getConfigDirectory() != null ) 261 { 262 reader.setConfigDirectory( new File( SvnUtil.getSettings().getConfigDirectory() ) ); 263 } 264 265 if ( StringUtils.isEmpty( reader.getProperty( "tunnels", tunnel ) ) ) 266 { 267 result.messages.add( 268 "The tunnel '" + tunnel + "' isn't defined in your subversion configuration file." ); 269 270 return result; 271 } 272 } 273 } 274 } 275 else if ( url.startsWith( "svn" ) ) 276 { 277 if ( !url.startsWith( "svn://" ) ) 278 { 279 result.messages.add( "A svn 'svn' url must be on the form 'svn://'." ); 280 281 return result; 282 } 283 } 284 else 285 { 286 result.messages.add( url + " url isn't a valid svn URL." ); 287 288 return result; 289 } 290 291 result.repository = new SvnScmProviderRepository( url ); 292 293 return result; 294 } 295 296 protected abstract SvnCommand getAddCommand(); 297 298 /** 299 * {@inheritDoc} 300 */ 301 public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 302 throws ScmException 303 { 304 return (AddScmResult) executeCommand( getAddCommand(), repository, fileSet, parameters ); 305 } 306 307 protected abstract SvnCommand getBranchCommand(); 308 309 /** 310 * {@inheritDoc} 311 */ 312 protected BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet, 313 CommandParameters parameters ) 314 throws ScmException 315 { 316 return (BranchScmResult) executeCommand( getBranchCommand(), repository, fileSet, parameters ); 317 } 318 319 protected abstract SvnCommand getChangeLogCommand(); 320 321 /** 322 * {@inheritDoc} 323 */ 324 public ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet, 325 CommandParameters parameters ) 326 throws ScmException 327 { 328 return (ChangeLogScmResult) executeCommand( getChangeLogCommand(), repository, fileSet, parameters ); 329 } 330 331 protected abstract SvnCommand getCheckInCommand(); 332 333 /** 334 * {@inheritDoc} 335 */ 336 public CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet, 337 CommandParameters parameters ) 338 throws ScmException 339 { 340 return (CheckInScmResult) executeCommand( getCheckInCommand(), repository, fileSet, parameters ); 341 } 342 343 protected abstract SvnCommand getCheckOutCommand(); 344 345 /** 346 * {@inheritDoc} 347 */ 348 public CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet, 349 CommandParameters parameters ) 350 throws ScmException 351 { 352 return (CheckOutScmResult) executeCommand( getCheckOutCommand(), repository, fileSet, parameters ); 353 } 354 355 protected abstract SvnCommand getDiffCommand(); 356 357 /** 358 * {@inheritDoc} 359 */ 360 public DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 361 throws ScmException 362 { 363 return (DiffScmResult) executeCommand( getDiffCommand(), repository, fileSet, parameters ); 364 } 365 366 protected abstract SvnCommand getExportCommand(); 367 368 /** 369 * {@inheritDoc} 370 */ 371 protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet, 372 CommandParameters parameters ) 373 throws ScmException 374 { 375 return (ExportScmResult) executeCommand( getExportCommand(), repository, fileSet, parameters ); 376 } 377 378 protected abstract SvnCommand getRemoveCommand(); 379 380 /** 381 * {@inheritDoc} 382 */ 383 public RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 384 throws ScmException 385 { 386 return (RemoveScmResult) executeCommand( getRemoveCommand(), repository, fileSet, parameters ); 387 } 388 389 protected abstract SvnCommand getStatusCommand(); 390 391 /** 392 * {@inheritDoc} 393 */ 394 public StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 395 throws ScmException 396 { 397 return (StatusScmResult) executeCommand( getStatusCommand(), repository, fileSet, parameters ); 398 } 399 400 protected abstract SvnCommand getTagCommand(); 401 402 /** 403 * {@inheritDoc} 404 */ 405 public TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 406 throws ScmException 407 { 408 return (TagScmResult) executeCommand( getTagCommand(), repository, fileSet, parameters ); 409 } 410 411 protected abstract SvnCommand getUpdateCommand(); 412 413 /** 414 * {@inheritDoc} 415 */ 416 public UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 417 throws ScmException 418 { 419 return (UpdateScmResult) executeCommand( getUpdateCommand(), repository, fileSet, parameters ); 420 } 421 422 protected ScmResult executeCommand( SvnCommand command, ScmProviderRepository repository, ScmFileSet fileSet, 423 CommandParameters parameters ) 424 throws ScmException 425 { 426 command.setLogger( getLogger() ); 427 428 return command.execute( repository, fileSet, parameters ); 429 } 430 431 protected abstract SvnCommand getListCommand(); 432 433 /** 434 * {@inheritDoc} 435 */ 436 public ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 437 throws ScmException 438 { 439 SvnCommand cmd = getListCommand(); 440 441 return (ListScmResult) executeCommand( cmd, repository, fileSet, parameters ); 442 } 443 444 protected abstract SvnCommand getInfoCommand(); 445 446 public InfoScmResult info( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 447 throws ScmException 448 { 449 SvnCommand cmd = getInfoCommand(); 450 451 return (InfoScmResult) executeCommand( cmd, repository, fileSet, parameters ); 452 } 453 454 /** 455 * {@inheritDoc} 456 */ 457 protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 458 throws ScmException 459 { 460 SvnCommand cmd = getBlameCommand(); 461 462 return (BlameScmResult) executeCommand( cmd, repository, fileSet, parameters ); 463 } 464 465 protected abstract SvnCommand getBlameCommand(); 466 467 /** 468 * {@inheritDoc} 469 */ 470 public MkdirScmResult mkdir( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 471 throws ScmException 472 { 473 SvnCommand cmd = getMkdirCommand(); 474 475 return (MkdirScmResult) executeCommand( cmd, repository, fileSet, parameters ); 476 } 477 478 protected abstract SvnCommand getMkdirCommand(); 479 480 /** 481 * @param repository 482 * @param parameters 483 * @return true if remote url exists 484 * @throws ScmException 485 * @since 1.8 486 */ 487 public abstract boolean remoteUrlExist( ScmProviderRepository repository, CommandParameters parameters ) 488 throws ScmException; 489}