001package org.apache.maven.scm.provider.accurev; 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.Collections; 025import java.util.Date; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.maven.scm.ChangeFile; 032import org.apache.maven.scm.ChangeSet; 033import org.apache.maven.scm.CommandParameter; 034import org.apache.maven.scm.CommandParameters; 035import org.apache.maven.scm.ScmException; 036import org.apache.maven.scm.ScmFile; 037import org.apache.maven.scm.ScmFileSet; 038import org.apache.maven.scm.ScmRevision; 039import org.apache.maven.scm.command.add.AddScmResult; 040import org.apache.maven.scm.command.blame.BlameScmResult; 041import org.apache.maven.scm.command.changelog.ChangeLogScmResult; 042import org.apache.maven.scm.command.checkin.CheckInScmResult; 043import org.apache.maven.scm.command.checkout.CheckOutScmResult; 044import org.apache.maven.scm.command.export.ExportScmResult; 045import org.apache.maven.scm.command.login.LoginScmResult; 046import org.apache.maven.scm.command.remove.RemoveScmResult; 047import org.apache.maven.scm.command.status.StatusScmResult; 048import org.apache.maven.scm.command.tag.TagScmResult; 049import org.apache.maven.scm.command.update.UpdateScmResult; 050import org.apache.maven.scm.provider.AbstractScmProvider; 051import org.apache.maven.scm.provider.ScmProviderRepository; 052import org.apache.maven.scm.provider.accurev.cli.AccuRevCommandLine; 053import org.apache.maven.scm.provider.accurev.command.add.AccuRevAddCommand; 054import org.apache.maven.scm.provider.accurev.command.blame.AccuRevBlameCommand; 055import org.apache.maven.scm.provider.accurev.command.changelog.AccuRevChangeLogCommand; 056import org.apache.maven.scm.provider.accurev.command.checkin.AccuRevCheckInCommand; 057import org.apache.maven.scm.provider.accurev.command.checkout.AccuRevCheckOutCommand; 058import org.apache.maven.scm.provider.accurev.command.export.AccuRevExportCommand; 059import org.apache.maven.scm.provider.accurev.command.login.AccuRevLoginCommand; 060import org.apache.maven.scm.provider.accurev.command.remove.AccuRevRemoveCommand; 061import org.apache.maven.scm.provider.accurev.command.status.AccuRevStatusCommand; 062import org.apache.maven.scm.provider.accurev.command.tag.AccuRevTagCommand; 063import org.apache.maven.scm.provider.accurev.command.update.AccuRevUpdateCommand; 064import org.apache.maven.scm.provider.accurev.command.update.AccuRevUpdateScmResult; 065import org.apache.maven.scm.provider.accurev.util.QuotedPropertyParser; 066import org.apache.maven.scm.repository.ScmRepositoryException; 067import org.apache.maven.scm.repository.UnknownRepositoryStructure; 068import org.codehaus.plexus.util.StringUtils; 069 070/** 071 * AccuRev integration with Maven SCM 072 * 073 * @plexus.component role="org.apache.maven.scm.provider.ScmProvider" role-hint="accurev" 074 */ 075public class AccuRevScmProvider 076 extends AbstractScmProvider 077{ 078 079 public static final String ACCUREV_EXECUTABLE_PROPERTY = "accurevExe"; 080 081 public static final String TAG_FORMAT_PROPERTY = "tagFormat"; 082 083 public static final String SYSTEM_PROPERTY_PREFIX = "maven.scm.accurev."; 084 085 public String getScmType() 086 { 087 088 return "accurev"; 089 } 090 091 /** 092 * The basic url parsing approach is to be as loose as possible. If you specify as per the docs you'll get what you 093 * expect. If you do something else the result is undefined. Don't use "/" "\" or "@" as the delimiter, 094 */ 095 public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter ) 096 throws ScmRepositoryException 097 { 098 099 List<String> validationMessages = new ArrayList<String>(); 100 101 String[] tokens = StringUtils.split( scmSpecificUrl, Character.toString( delimiter ) ); 102 103 // [[user][/pass]@host[:port]][:stream][:\project\dir] 104 105 String basisStream = null; 106 String projectPath = null; 107 int port = AccuRev.DEFAULT_PORT; 108 String host = null; 109 String user = null; 110 String password = null; 111 Map<String, String> properties = new HashMap<String, String>(); 112 properties.put( TAG_FORMAT_PROPERTY, AccuRevScmProviderRepository.DEFAULT_TAG_FORMAT ); 113 properties.put( ACCUREV_EXECUTABLE_PROPERTY, AccuRev.DEFAULT_ACCUREV_EXECUTABLE ); 114 115 fillSystemProperties( properties ); 116 117 int i = 0; 118 while ( i < tokens.length ) 119 { 120 int at = tokens[i].indexOf( '@' ); 121 // prefer "/", better not have a "/" or a "\\" in your password. 122 int slash = tokens[i].indexOf( '/' ); 123 slash = slash < 0 ? tokens[i].indexOf( '\\' ) : slash; 124 125 int qMark = tokens[i].indexOf( '?' ); 126 127 if ( qMark == 0 ) 128 { 129 QuotedPropertyParser.parse( tokens[i].substring( 1 ), properties ); 130 } 131 else if ( slash == 0 ) 132 { 133 // this is the project path 134 projectPath = tokens[i].substring( 1 ); 135 break; 136 } 137 else if ( ( slash > 0 || ( at >= 0 ) ) && host == null && user == null ) 138 { 139 // user/pass@host 140 int len = tokens[i].length(); 141 if ( at >= 0 && len > at ) 142 { 143 // everything after the "@" 144 host = tokens[i].substring( at + 1 ); 145 } 146 147 if ( slash > 0 ) 148 { 149 // user up to / 150 user = tokens[i].substring( 0, slash ); 151 // pass between / and @ 152 password = tokens[i].substring( slash + 1, at < 0 ? len : at ); 153 } 154 else 155 { 156 // no /, user from beginning to @ 157 user = tokens[i].substring( 0, at < 0 ? len : at ); 158 } 159 160 } 161 else if ( host != null && tokens[i].matches( "^[0-9]+$" ) ) 162 { 163 // only valid entry with all digits is the port specification. 164 port = Integer.parseInt( tokens[i] ); 165 } 166 else 167 { 168 basisStream = tokens[i]; 169 } 170 171 i++; 172 } 173 174 if ( i < tokens.length ) 175 { 176 validationMessages.add( "Unknown tokens in URL " + scmSpecificUrl ); 177 } 178 179 AccuRevScmProviderRepository repo = new AccuRevScmProviderRepository(); 180 repo.setLogger( getLogger() ); 181 if ( !StringUtils.isEmpty( user ) ) 182 { 183 repo.setUser( user ); 184 } 185 if ( !StringUtils.isEmpty( password ) ) 186 { 187 repo.setPassword( password ); 188 } 189 if ( !StringUtils.isEmpty( basisStream ) ) 190 { 191 repo.setStreamName( basisStream ); 192 } 193 if ( !StringUtils.isEmpty( projectPath ) ) 194 { 195 repo.setProjectPath( projectPath ); 196 } 197 if ( !StringUtils.isEmpty( host ) ) 198 { 199 repo.setHost( host ); 200 } 201 repo.setPort( port ); 202 repo.setTagFormat( properties.get( TAG_FORMAT_PROPERTY ) ); 203 204 AccuRevCommandLine accuRev = new AccuRevCommandLine( host, port ); 205 accuRev.setLogger( getLogger() ); 206 accuRev.setExecutable( properties.get( ACCUREV_EXECUTABLE_PROPERTY ) ); 207 repo.setAccuRev( accuRev ); 208 209 return repo; 210 211 } 212 213 private void fillSystemProperties( Map<String, String> properties ) 214 { 215 216 Set<String> propertyKeys = properties.keySet(); 217 for ( String key : propertyKeys ) 218 { 219 String systemPropertyKey = SYSTEM_PROPERTY_PREFIX + key; 220 String systemProperty = System.getProperty( systemPropertyKey ); 221 if ( systemProperty != null ) 222 { 223 properties.put( key, systemProperty ); 224 } 225 } 226 227 } 228 229 @Override 230 protected LoginScmResult login( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 231 throws ScmException 232 { 233 234 if ( getLogger().isDebugEnabled() ) 235 { 236 getLogger().debug( repository.toString() ); 237 } 238 239 AccuRevLoginCommand command = new AccuRevLoginCommand( getLogger() ); 240 return command.login( repository, fileSet, parameters ); 241 } 242 243 @Override 244 protected CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet, 245 CommandParameters parameters ) 246 throws ScmException 247 { 248 249 // workaround deprecated behaviour 250 // TODO pull up to AbstractScmProvider 251 AccuRevScmProviderRepository accuRevRepo = (AccuRevScmProviderRepository) repository; 252 if ( !repository.isPersistCheckout() && accuRevRepo.shouldUseExportForNonPersistentCheckout() ) 253 { 254 255 ExportScmResult result = export( repository, fileSet, parameters ); 256 if ( result.isSuccess() ) 257 { 258 return new CheckOutScmResult( result.getCommandLine(), result.getExportedFiles(), 259 accuRevRepo.getExportRelativePath() ); 260 } 261 else 262 { 263 return new CheckOutScmResult( result.getCommandLine(), result.getProviderMessage(), 264 result.getCommandOutput(), false ); 265 } 266 } 267 268 AccuRevCheckOutCommand command = new AccuRevCheckOutCommand( getLogger() ); 269 270 return command.checkout( repository, fileSet, parameters ); 271 272 } 273 274 @Override 275 protected CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet, 276 CommandParameters parameters ) 277 throws ScmException 278 { 279 280 AccuRevCheckInCommand command = new AccuRevCheckInCommand( getLogger() ); 281 282 return command.checkIn( repository, fileSet, parameters ); 283 } 284 285 @Override 286 public ScmProviderRepository makeProviderScmRepository( File path ) 287 throws ScmRepositoryException, UnknownRepositoryStructure 288 { 289 290 // TODO: accurev info with current dir = "path", find workspace. Find use-case for this. 291 return super.makeProviderScmRepository( path ); 292 } 293 294 @Override 295 public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 296 throws ScmException 297 { 298 AccuRevAddCommand command = new AccuRevAddCommand( getLogger() ); 299 return command.add( repository, fileSet, parameters ); 300 } 301 302 @Override 303 protected TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 304 throws ScmException 305 { 306 307 AccuRevTagCommand command = new AccuRevTagCommand( getLogger() ); 308 return command.tag( repository, fileSet, parameters ); 309 310 } 311 312 @Override 313 protected StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 314 throws ScmException 315 { 316 317 AccuRevStatusCommand command = new AccuRevStatusCommand( getLogger() ); 318 return command.status( repository, fileSet, parameters ); 319 320 } 321 322 @Override 323 protected UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 324 throws ScmException 325 { 326 327 AccuRevScmProviderRepository accurevRepo = (AccuRevScmProviderRepository) repository; 328 329 AccuRevUpdateCommand command = new AccuRevUpdateCommand( getLogger() ); 330 331 UpdateScmResult result = command.update( repository, fileSet, parameters ); 332 333 if ( result.isSuccess() && parameters.getBoolean( CommandParameter.RUN_CHANGELOG_WITH_UPDATE ) ) 334 { 335 AccuRevUpdateScmResult accuRevResult = (AccuRevUpdateScmResult) result; 336 337 ScmRevision fromRevision = new ScmRevision( accuRevResult.getFromRevision() ); 338 ScmRevision toRevision = new ScmRevision( accuRevResult.getToRevision() ); 339 340 parameters.setScmVersion( CommandParameter.START_SCM_VERSION, fromRevision ); 341 parameters.setScmVersion( CommandParameter.END_SCM_VERSION, toRevision ); 342 343 AccuRevVersion startVersion = accurevRepo.getAccuRevVersion( fromRevision ); 344 AccuRevVersion endVersion = accurevRepo.getAccuRevVersion( toRevision ); 345 if ( startVersion.getBasisStream().equals( endVersion.getBasisStream() ) ) 346 { 347 ChangeLogScmResult changeLogResult = changelog( repository, fileSet, parameters ); 348 349 if ( changeLogResult.isSuccess() ) 350 { 351 result.setChanges( changeLogResult.getChangeLog().getChangeSets() ); 352 } 353 else 354 { 355 getLogger().warn( "Changelog from " + fromRevision + " to " + toRevision + " failed" ); 356 } 357 } 358 else 359 { 360 String comment = "Cross stream update result from " + startVersion + " to " + endVersion; 361 String author = ""; 362 List<ScmFile> files = result.getUpdatedFiles(); 363 List<ChangeFile> changeFiles = new ArrayList<ChangeFile>( files.size() ); 364 for (ScmFile scmFile : files) 365 { 366 changeFiles.add(new ChangeFile( scmFile.getPath() )); 367 } 368 ChangeSet dummyChangeSet = new ChangeSet( new Date(), comment, author, changeFiles ); 369 // different streams invalidates the change log, insert a dummy change instead. 370 List<ChangeSet> changeSets = Collections.singletonList( dummyChangeSet ); 371 result.setChanges( changeSets ); 372 } 373 374 } 375 return result; 376 } 377 378 @Override 379 protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 380 throws ScmException 381 { 382 383 AccuRevExportCommand command = new AccuRevExportCommand( getLogger() ); 384 return command.export( repository, fileSet, parameters ); 385 } 386 387 @Override 388 protected ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet, 389 CommandParameters parameters ) 390 throws ScmException 391 { 392 393 AccuRevChangeLogCommand command = new AccuRevChangeLogCommand( getLogger() ); 394 return command.changelog( repository, fileSet, parameters ); 395 } 396 397 @Override 398 protected RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 399 throws ScmException 400 { 401 402 AccuRevRemoveCommand command = new AccuRevRemoveCommand( getLogger() ); 403 return command.remove( repository, fileSet, parameters ); 404 } 405 406 /** {@inheritDoc} */ 407 @Override 408 protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 409 throws ScmException 410 { 411 412 AccuRevBlameCommand blameCommand = new AccuRevBlameCommand( getLogger() ); 413 return blameCommand.blame( repository, fileSet, parameters ); 414 } 415}