001package org.apache.maven.scm.provider.jazz; 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 org.apache.maven.scm.CommandParameters; 023import org.apache.maven.scm.ScmException; 024import org.apache.maven.scm.ScmFileSet; 025import org.apache.maven.scm.command.add.AddScmResult; 026import org.apache.maven.scm.command.blame.BlameScmResult; 027import org.apache.maven.scm.command.branch.BranchScmResult; 028import org.apache.maven.scm.command.changelog.ChangeLogScmResult; 029import org.apache.maven.scm.command.checkin.CheckInScmResult; 030import org.apache.maven.scm.command.checkout.CheckOutScmResult; 031import org.apache.maven.scm.command.diff.DiffScmResult; 032import org.apache.maven.scm.command.edit.EditScmResult; 033import org.apache.maven.scm.command.export.ExportScmResult; 034import org.apache.maven.scm.command.list.ListScmResult; 035import org.apache.maven.scm.command.status.StatusScmResult; 036import org.apache.maven.scm.command.tag.TagScmResult; 037import org.apache.maven.scm.command.unedit.UnEditScmResult; 038import org.apache.maven.scm.command.update.UpdateScmResult; 039import org.apache.maven.scm.provider.AbstractScmProvider; 040import org.apache.maven.scm.provider.ScmProviderRepository; 041import org.apache.maven.scm.provider.jazz.command.JazzConstants; 042import org.apache.maven.scm.provider.jazz.command.add.JazzAddCommand; 043import org.apache.maven.scm.provider.jazz.command.blame.JazzBlameCommand; 044import org.apache.maven.scm.provider.jazz.command.branch.JazzBranchCommand; 045import org.apache.maven.scm.provider.jazz.command.changelog.JazzChangeLogCommand; 046import org.apache.maven.scm.provider.jazz.command.checkin.JazzCheckInCommand; 047import org.apache.maven.scm.provider.jazz.command.checkout.JazzCheckOutCommand; 048import org.apache.maven.scm.provider.jazz.command.diff.JazzDiffCommand; 049import org.apache.maven.scm.provider.jazz.command.edit.JazzEditCommand; 050import org.apache.maven.scm.provider.jazz.command.list.JazzListCommand; 051import org.apache.maven.scm.provider.jazz.command.status.JazzStatusCommand; 052import org.apache.maven.scm.provider.jazz.command.tag.JazzTagCommand; 053import org.apache.maven.scm.provider.jazz.command.unedit.JazzUnEditCommand; 054import org.apache.maven.scm.provider.jazz.command.update.JazzUpdateCommand; 055import org.apache.maven.scm.provider.jazz.repository.JazzScmProviderRepository; 056import org.apache.maven.scm.repository.ScmRepositoryException; 057 058import java.net.URI; 059 060/** 061 * The maven scm provider for Jazz. 062 * <p/> 063 * This provider is a wrapper for the command line tool, "scm.sh" or "scm.exe" is that is 064 * part of the Jazz SCM Server. 065 * <p/> 066 * This provider does not use a native API to communicate with the Jazz SCM server. 067 * <p/> 068 * The scm tool itself is documented at: 069 * V2.0.0 - http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_scm.html 070 * V3.0 - http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_scm.html 071 * V3.0.1 - http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_scm.html 072 * 073 * @author <a href="mailto:ChrisGWarp@gmail.com">Chris Graham</a> 074 * @plexus.component role="org.apache.maven.scm.provider.ScmProvider" role-hint="jazz" 075 */ 076public class JazzScmProvider 077 extends AbstractScmProvider 078{ 079 // Example: scm:jazz:daviddl;passw0rd123@https://localhost:9443/jazz:Dave's Repository Workspace 080 // If the username or password is supplied, then the @ must be used to delimit them. 081 public static final String JAZZ_URL_FORMAT = 082 "scm:jazz:[username[;password]@]http[s]://server_name[:port]/contextRoot:repositoryWorkspace"; 083 084 // ---------------------------------------------------------------------- 085 // ScmProvider Implementation 086 // ---------------------------------------------------------------------- 087 088 public String getScmType() 089 { 090 return JazzConstants.SCM_TYPE; 091 } 092 093 /** 094 * This method parses the scm URL and returns a SCM provider repository. 095 * At this point, the scmUrl is the part after scm:provider_name: in your SCM URL. 096 * <p/> 097 * The basic url parsing approach is to be as loose as possible. 098 * If you specify as per the docs you'll get what you expect. 099 * If you do something else the result is undefined. 100 * Don't use "/" "\" or "@" as the delimiter. 101 * <p/> 102 * Parse the scmUrl, which will be of the form: 103 * [username[;password]@]http[s]://server_name[:port]/contextRoot:repositoryWorkspace 104 * eg: 105 * Deb;Deb@https://rtc:9444/jazz:BogusRepositoryWorkspace 106 * {@inheritDoc} 107 */ 108 public ScmProviderRepository makeProviderScmRepository( String scmUrl, char delimiter ) 109 throws ScmRepositoryException 110 { 111 // Called from: 112 // AbstractScmProvider.makeScmRepository() 113 // AbstractScmProvider.validateScmUrl() 114 getLogger().debug( "JazzScmProvider:makeProviderScmRepository" ); 115 getLogger().debug( "Provided scm url - " + scmUrl ); 116 getLogger().debug( "Provided delimiter - '" + delimiter + "'" ); 117 118 String jazzUrlAndWorkspace = null; 119 String usernameAndPassword = null; 120 121 // Look for the Jazz URL after any '@' delimiter used to pass 122 // username/password etc (which may not have been specified) 123 int lastAtPosition = scmUrl.lastIndexOf( '@' ); 124 if ( lastAtPosition == -1 ) 125 { 126 // The username;password@ was not supplied. 127 jazzUrlAndWorkspace = scmUrl; 128 } 129 else 130 { 131 // The username@ or username;password@ was supplied. 132 jazzUrlAndWorkspace = ( lastAtPosition < 0 ) ? scmUrl : scmUrl.substring( lastAtPosition + 1 ); 133 usernameAndPassword = ( lastAtPosition < 0 ) ? null : scmUrl.substring( 0, lastAtPosition ); 134 } 135 136 // jazzUrlAndWorkspace should be: http[s]://server_name:port/contextRoot:repositoryWorkspace 137 // usernameAndPassword should be: username;password or null 138 139 // username and password may not be supplied, and so may remain null. 140 String username = null; 141 String password = null; 142 143 if ( usernameAndPassword != null ) 144 { 145 // Can be: 146 // username 147 // username;password 148 int delimPosition = usernameAndPassword.indexOf( ';' ); 149 username = delimPosition >= 0 ? usernameAndPassword.substring( 0, delimPosition ) : usernameAndPassword; 150 password = delimPosition >= 0 ? usernameAndPassword.substring( delimPosition + 1 ) : null; 151 } 152 153 // We will now validate the jazzUrlAndWorkspace for right number of colons. 154 // This has been observed in the wild, where the contextRoot:repositoryWorkspace was not properly formed 155 // and this resulted in very strange results in the way in which things were parsed. 156 int colonsCounted = 0; 157 int colonIndex = 0; 158 while ( colonIndex != -1 ) 159 { 160 colonIndex = jazzUrlAndWorkspace.indexOf( ":", colonIndex + 1 ); 161 if ( colonIndex != -1 ) 162 { 163 colonsCounted++; 164 } 165 } 166 // havePort may also be true when port is supplied, but otherwise have a malformed URL. 167 boolean havePort = colonsCounted == 3; 168 169 // Look for workspace after the end of the Jazz URL 170 int repositoryWorkspacePosition = jazzUrlAndWorkspace.lastIndexOf( delimiter ); 171 String repositoryWorkspace = jazzUrlAndWorkspace.substring( repositoryWorkspacePosition + 1 ); 172 String jazzUrl = jazzUrlAndWorkspace.substring( 0, repositoryWorkspacePosition ); 173 174 // Validate the protocols. 175 try 176 { 177 // Determine if it is a valid URI. 178 URI jazzUri = URI.create( jazzUrl ); 179 String scheme = jazzUri.getScheme(); 180 getLogger().debug( "Scheme - " + scheme ); 181 if ( scheme == null || !( scheme.equalsIgnoreCase( "http" ) || scheme.equalsIgnoreCase( "https" ) ) ) 182 { 183 throw new ScmRepositoryException( 184 "Jazz Url \"" + jazzUrl + "\" is not a valid URL. The Jazz Url syntax is " + JAZZ_URL_FORMAT ); 185 } 186 } 187 catch ( IllegalArgumentException e ) 188 { 189 throw new ScmRepositoryException( 190 "Jazz Url \"" + jazzUrl + "\" is not a valid URL. The Jazz Url syntax is " + JAZZ_URL_FORMAT ); 191 } 192 193 // At this point, jazzUrl is guaranteed to start with either http:// or https:// 194 // Further process the jazzUrl to extract the server name and port. 195 String hostname = null; 196 int port = 0; 197 198 if ( havePort ) 199 { 200 // jazzUrlAndWorkspace should be: http[s]://server_name:port/contextRoot:repositoryWorkspace 201 // jazzUrl should be : http[s]://server_name:port/contextRoot 202 int protocolIndex = jazzUrl.indexOf( ":" ) + 3; // The +3 accounts for the "://" 203 int portIndex = jazzUrl.indexOf( ":", protocolIndex + 1 ); 204 hostname = jazzUrl.substring( protocolIndex, portIndex ); 205 int pathIndex = jazzUrl.indexOf( "/", portIndex + 1 ); 206 String portNo = jazzUrl.substring( portIndex + 1, pathIndex ); 207 try 208 { 209 port = Integer.parseInt( portNo ); 210 } 211 catch ( NumberFormatException nfe ) 212 { 213 throw new ScmRepositoryException( 214 "Jazz Url \"" + jazzUrl + "\" is not a valid URL. The Jazz Url syntax is " + JAZZ_URL_FORMAT ); 215 } 216 } 217 else 218 { 219 // jazzUrlAndWorkspace should be: http[s]://server_name/contextRoot:repositoryWorkspace 220 // jazzUrl should be : http[s]://server_name/contextRoot 221 // So we will set port to zero. 222 int protocolIndex = jazzUrl.indexOf( ":" ) + 3; // The +3 accounts for the "://" 223 int pathIndex = jazzUrl.indexOf( "/", protocolIndex + 1 ); 224 if ( ( protocolIndex != -1 ) && ( pathIndex != -1 ) ) 225 { 226 hostname = jazzUrl.substring( protocolIndex, pathIndex ); 227 } 228 else 229 { 230 throw new ScmRepositoryException( 231 "Jazz Url \"" + jazzUrl + "\" is not a valid URL. The Jazz Url syntax is " + JAZZ_URL_FORMAT ); 232 } 233 } 234 235 getLogger().debug( "Creating JazzScmProviderRepository with the following values:" ); 236 getLogger().debug( "jazzUrl - " + jazzUrl ); 237 getLogger().debug( "username - " + username ); 238 getLogger().debug( "password - " + password ); 239 getLogger().debug( "hostname - " + hostname ); 240 getLogger().debug( "port - " + port ); 241 getLogger().debug( "repositoryWorkspace - " + repositoryWorkspace ); 242 243 return new JazzScmProviderRepository( jazzUrl, username, password, hostname, port, repositoryWorkspace ); 244 } 245 246 /** 247 * {@inheritDoc} 248 */ 249 public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 250 throws ScmException 251 { 252 JazzAddCommand command = new JazzAddCommand(); 253 command.setLogger( getLogger() ); 254 return (AddScmResult) command.execute( repository, fileSet, parameters ); 255 } 256 257 /** 258 * {@inheritDoc} 259 */ 260 protected BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet, 261 CommandParameters parameters ) 262 throws ScmException 263 { 264 JazzBranchCommand command = new JazzBranchCommand(); 265 command.setLogger( getLogger() ); 266 return (BranchScmResult) command.execute( repository, fileSet, parameters ); 267 } 268 269 /** 270 * {@inheritDoc} 271 */ 272 protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 273 throws ScmException 274 { 275 JazzBlameCommand command = new JazzBlameCommand(); 276 command.setLogger( getLogger() ); 277 return (BlameScmResult) command.execute( repository, fileSet, parameters ); 278 } 279 280 /** 281 * {@inheritDoc} 282 */ 283 protected ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet, 284 CommandParameters parameters ) 285 throws ScmException 286 { 287 // We need to call the status command first, so that we can get the details of the workspace. 288 // This is needed for the list changesets command. 289 // We could also 'trust' the value in the pom. 290 JazzStatusCommand statusCommand = new JazzStatusCommand(); 291 statusCommand.setLogger( getLogger() ); 292 statusCommand.execute( repository, fileSet, parameters ); 293 294 JazzChangeLogCommand command = new JazzChangeLogCommand(); 295 command.setLogger( getLogger() ); 296 return (ChangeLogScmResult) command.execute( repository, fileSet, parameters ); 297 } 298 299 /** 300 * {@inheritDoc} 301 */ 302 protected CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet, 303 CommandParameters parameters ) 304 throws ScmException 305 { 306 JazzCheckInCommand command = new JazzCheckInCommand(); 307 command.setLogger( getLogger() ); 308 return (CheckInScmResult) command.execute( repository, fileSet, parameters ); 309 } 310 311 /** 312 * {@inheritDoc} 313 */ 314 protected CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet, 315 CommandParameters parameters ) 316 throws ScmException 317 { 318 JazzCheckOutCommand command = new JazzCheckOutCommand(); 319 command.setLogger( getLogger() ); 320 return (CheckOutScmResult) command.execute( repository, fileSet, parameters ); 321 } 322 323 /** 324 * {@inheritDoc} 325 */ 326 protected DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 327 throws ScmException 328 { 329 JazzDiffCommand command = new JazzDiffCommand(); 330 command.setLogger( getLogger() ); 331 return (DiffScmResult) command.execute( repository, fileSet, parameters ); 332 } 333 334 /** 335 * {@inheritDoc} 336 */ 337 protected EditScmResult edit( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 338 throws ScmException 339 { 340 JazzEditCommand command = new JazzEditCommand(); 341 command.setLogger( getLogger() ); 342 return (EditScmResult) command.execute( repository, fileSet, parameters ); 343 } 344 345 /** 346 * {@inheritDoc} 347 */ 348 protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet, 349 CommandParameters parameters ) 350 throws ScmException 351 { 352 // Use checkout instead 353 return super.export( repository, fileSet, parameters ); 354 } 355 356 /** 357 * {@inheritDoc} 358 */ 359 protected ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 360 throws ScmException 361 { 362 // We need to call the status command first, so that we can get the details of the stream etc. 363 // This is needed for workspace deliveries and snapshot promotions. 364 JazzStatusCommand statusCommand = new JazzStatusCommand(); 365 statusCommand.setLogger( getLogger() ); 366 statusCommand.execute( repository, fileSet, parameters ); 367 368 JazzListCommand command = new JazzListCommand(); 369 command.setLogger( getLogger() ); 370 return (ListScmResult) command.execute( repository, fileSet, parameters ); 371 } 372 373 /** 374 * {@inheritDoc} 375 */ 376 protected StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, 377 CommandParameters parameters ) 378 throws ScmException 379 { 380 JazzStatusCommand command = new JazzStatusCommand(); 381 command.setLogger( getLogger() ); 382 return (StatusScmResult) command.execute( repository, fileSet, parameters ); 383 } 384 385 /** 386 * {@inheritDoc} 387 */ 388 protected TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) 389 throws ScmException 390 { 391 // We need to call the status command first, so that we can get the details of the stream etc. 392 // This is needed for workspace deliveries and snapshot promotions. 393 JazzStatusCommand statusCommand = new JazzStatusCommand(); 394 statusCommand.setLogger( getLogger() ); 395 statusCommand.execute( repository, fileSet, parameters ); 396 397 JazzTagCommand command = new JazzTagCommand(); 398 command.setLogger( getLogger() ); 399 return (TagScmResult) command.execute( repository, fileSet, parameters ); 400 } 401 402 /** 403 * {@inheritDoc} 404 */ 405 protected UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, 406 CommandParameters parameters ) 407 throws ScmException 408 { 409 JazzUpdateCommand command = new JazzUpdateCommand(); 410 command.setLogger( getLogger() ); 411 return (UpdateScmResult) command.execute( repository, fileSet, parameters ); 412 } 413 414 /** 415 * {@inheritDoc} 416 */ 417 protected UnEditScmResult unedit( ScmProviderRepository repository, ScmFileSet fileSet, 418 CommandParameters parameters ) 419 throws ScmException 420 { 421 JazzUnEditCommand command = new JazzUnEditCommand(); 422 command.setLogger( getLogger() ); 423 return (UnEditScmResult) command.execute( repository, fileSet, parameters ); 424 } 425 426 /** 427 * {@inheritDoc} 428 */ 429 public String getScmSpecificFilename() 430 { 431 return JazzConstants.SCM_META_DATA_FOLDER; 432 } 433 434}