| package org.apache.maven.scm.provider.jazz; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import org.apache.maven.scm.CommandParameters; |
| import org.apache.maven.scm.ScmException; |
| import org.apache.maven.scm.ScmFileSet; |
| import org.apache.maven.scm.command.add.AddScmResult; |
| import org.apache.maven.scm.command.blame.BlameScmResult; |
| import org.apache.maven.scm.command.branch.BranchScmResult; |
| import org.apache.maven.scm.command.changelog.ChangeLogScmResult; |
| import org.apache.maven.scm.command.checkin.CheckInScmResult; |
| import org.apache.maven.scm.command.checkout.CheckOutScmResult; |
| import org.apache.maven.scm.command.diff.DiffScmResult; |
| import org.apache.maven.scm.command.edit.EditScmResult; |
| import org.apache.maven.scm.command.export.ExportScmResult; |
| import org.apache.maven.scm.command.list.ListScmResult; |
| import org.apache.maven.scm.command.status.StatusScmResult; |
| import org.apache.maven.scm.command.tag.TagScmResult; |
| import org.apache.maven.scm.command.unedit.UnEditScmResult; |
| import org.apache.maven.scm.command.update.UpdateScmResult; |
| import org.apache.maven.scm.provider.AbstractScmProvider; |
| import org.apache.maven.scm.provider.ScmProviderRepository; |
| import org.apache.maven.scm.provider.jazz.command.JazzConstants; |
| import org.apache.maven.scm.provider.jazz.command.add.JazzAddCommand; |
| import org.apache.maven.scm.provider.jazz.command.blame.JazzBlameCommand; |
| import org.apache.maven.scm.provider.jazz.command.branch.JazzBranchCommand; |
| import org.apache.maven.scm.provider.jazz.command.changelog.JazzChangeLogCommand; |
| import org.apache.maven.scm.provider.jazz.command.checkin.JazzCheckInCommand; |
| import org.apache.maven.scm.provider.jazz.command.checkout.JazzCheckOutCommand; |
| import org.apache.maven.scm.provider.jazz.command.diff.JazzDiffCommand; |
| import org.apache.maven.scm.provider.jazz.command.edit.JazzEditCommand; |
| import org.apache.maven.scm.provider.jazz.command.list.JazzListCommand; |
| import org.apache.maven.scm.provider.jazz.command.status.JazzStatusCommand; |
| import org.apache.maven.scm.provider.jazz.command.tag.JazzTagCommand; |
| import org.apache.maven.scm.provider.jazz.command.unedit.JazzUnEditCommand; |
| import org.apache.maven.scm.provider.jazz.command.update.JazzUpdateCommand; |
| import org.apache.maven.scm.provider.jazz.repository.JazzScmProviderRepository; |
| import org.apache.maven.scm.repository.ScmRepositoryException; |
| |
| import java.net.URI; |
| |
| /** |
| * The maven scm provider for Jazz. |
| * <p/> |
| * This provider is a wrapper for the command line tool, "scm.sh" or "scm.exe" is that is |
| * part of the Jazz SCM Server. |
| * <p/> |
| * This provider does not use a native API to communicate with the Jazz SCM server. |
| * <p/> |
| * The scm tool itself is documented at: |
| * V2.0.0 - http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_scm.html |
| * V3.0 - http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_scm.html |
| * V3.0.1 - http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_scm.html |
| * |
| * @author <a href="mailto:ChrisGWarp@gmail.com">Chris Graham</a> |
| * @plexus.component role="org.apache.maven.scm.provider.ScmProvider" role-hint="jazz" |
| */ |
| public class JazzScmProvider |
| extends AbstractScmProvider |
| { |
| // Example: scm:jazz:daviddl;passw0rd123@https://localhost:9443/jazz:Dave's Repository Workspace |
| // If the username or password is supplied, then the @ must be used to delimit them. |
| public static final String JAZZ_URL_FORMAT = |
| "scm:jazz:[username[;password]@]http[s]://server_name[:port]/contextRoot:repositoryWorkspace"; |
| |
| // ---------------------------------------------------------------------- |
| // ScmProvider Implementation |
| // ---------------------------------------------------------------------- |
| |
| public String getScmType() |
| { |
| return JazzConstants.SCM_TYPE; |
| } |
| |
| /** |
| * This method parses the scm URL and returns a SCM provider repository. |
| * At this point, the scmUrl is the part after scm:provider_name: in your SCM URL. |
| * <p/> |
| * The basic url parsing approach is to be as loose as possible. |
| * If you specify as per the docs you'll get what you expect. |
| * If you do something else the result is undefined. |
| * Don't use "/" "\" or "@" as the delimiter. |
| * <p/> |
| * Parse the scmUrl, which will be of the form: |
| * [username[;password]@]http[s]://server_name[:port]/contextRoot:repositoryWorkspace |
| * eg: |
| * Deb;Deb@https://rtc:9444/jazz:BogusRepositoryWorkspace |
| * {@inheritDoc} |
| */ |
| public ScmProviderRepository makeProviderScmRepository( String scmUrl, char delimiter ) |
| throws ScmRepositoryException |
| { |
| // Called from: |
| // AbstractScmProvider.makeScmRepository() |
| // AbstractScmProvider.validateScmUrl() |
| getLogger().debug( "JazzScmProvider:makeProviderScmRepository" ); |
| getLogger().debug( "Provided scm url - " + scmUrl ); |
| getLogger().debug( "Provided delimiter - '" + delimiter + "'" ); |
| |
| String jazzUrlAndWorkspace = null; |
| String usernameAndPassword = null; |
| |
| // Look for the Jazz URL after any '@' delimiter used to pass |
| // username/password etc (which may not have been specified) |
| int lastAtPosition = scmUrl.lastIndexOf( '@' ); |
| if ( lastAtPosition == -1 ) |
| { |
| // The username;password@ was not supplied. |
| jazzUrlAndWorkspace = scmUrl; |
| } |
| else |
| { |
| // The username@ or username;password@ was supplied. |
| jazzUrlAndWorkspace = ( lastAtPosition < 0 ) ? scmUrl : scmUrl.substring( lastAtPosition + 1 ); |
| usernameAndPassword = ( lastAtPosition < 0 ) ? null : scmUrl.substring( 0, lastAtPosition ); |
| } |
| |
| // jazzUrlAndWorkspace should be: http[s]://server_name:port/contextRoot:repositoryWorkspace |
| // usernameAndPassword should be: username;password or null |
| |
| // username and password may not be supplied, and so may remain null. |
| String username = null; |
| String password = null; |
| |
| if ( usernameAndPassword != null ) |
| { |
| // Can be: |
| // username |
| // username;password |
| int delimPosition = usernameAndPassword.indexOf( ';' ); |
| username = delimPosition >= 0 ? usernameAndPassword.substring( 0, delimPosition ) : usernameAndPassword; |
| password = delimPosition >= 0 ? usernameAndPassword.substring( delimPosition + 1 ) : null; |
| } |
| |
| // We will now validate the jazzUrlAndWorkspace for right number of colons. |
| // This has been observed in the wild, where the contextRoot:repositoryWorkspace was not properly formed |
| // and this resulted in very strange results in the way in which things were parsed. |
| int colonsCounted = 0; |
| int colonIndex = 0; |
| while ( colonIndex != -1 ) |
| { |
| colonIndex = jazzUrlAndWorkspace.indexOf( ":", colonIndex + 1 ); |
| if ( colonIndex != -1 ) |
| { |
| colonsCounted++; |
| } |
| } |
| // havePort may also be true when port is supplied, but otherwise have a malformed URL. |
| boolean havePort = colonsCounted == 3; |
| |
| // Look for workspace after the end of the Jazz URL |
| int repositoryWorkspacePosition = jazzUrlAndWorkspace.lastIndexOf( delimiter ); |
| String repositoryWorkspace = jazzUrlAndWorkspace.substring( repositoryWorkspacePosition + 1 ); |
| String jazzUrl = jazzUrlAndWorkspace.substring( 0, repositoryWorkspacePosition ); |
| |
| // Validate the protocols. |
| try |
| { |
| // Determine if it is a valid URI. |
| URI jazzUri = URI.create( jazzUrl ); |
| String scheme = jazzUri.getScheme(); |
| getLogger().debug( "Scheme - " + scheme ); |
| if ( scheme == null || !( scheme.equalsIgnoreCase( "http" ) || scheme.equalsIgnoreCase( "https" ) ) ) |
| { |
| throw new ScmRepositoryException( |
| "Jazz Url \"" + jazzUrl + "\" is not a valid URL. The Jazz Url syntax is " + JAZZ_URL_FORMAT ); |
| } |
| } |
| catch ( IllegalArgumentException e ) |
| { |
| throw new ScmRepositoryException( |
| "Jazz Url \"" + jazzUrl + "\" is not a valid URL. The Jazz Url syntax is " + JAZZ_URL_FORMAT ); |
| } |
| |
| // At this point, jazzUrl is guaranteed to start with either http:// or https:// |
| // Further process the jazzUrl to extract the server name and port. |
| String hostname = null; |
| int port = 0; |
| |
| if ( havePort ) |
| { |
| // jazzUrlAndWorkspace should be: http[s]://server_name:port/contextRoot:repositoryWorkspace |
| // jazzUrl should be : http[s]://server_name:port/contextRoot |
| int protocolIndex = jazzUrl.indexOf( ":" ) + 3; // The +3 accounts for the "://" |
| int portIndex = jazzUrl.indexOf( ":", protocolIndex + 1 ); |
| hostname = jazzUrl.substring( protocolIndex, portIndex ); |
| int pathIndex = jazzUrl.indexOf( "/", portIndex + 1 ); |
| String portNo = jazzUrl.substring( portIndex + 1, pathIndex ); |
| try |
| { |
| port = Integer.parseInt( portNo ); |
| } |
| catch ( NumberFormatException nfe ) |
| { |
| throw new ScmRepositoryException( |
| "Jazz Url \"" + jazzUrl + "\" is not a valid URL. The Jazz Url syntax is " + JAZZ_URL_FORMAT ); |
| } |
| } |
| else |
| { |
| // jazzUrlAndWorkspace should be: http[s]://server_name/contextRoot:repositoryWorkspace |
| // jazzUrl should be : http[s]://server_name/contextRoot |
| // So we will set port to zero. |
| int protocolIndex = jazzUrl.indexOf( ":" ) + 3; // The +3 accounts for the "://" |
| int pathIndex = jazzUrl.indexOf( "/", protocolIndex + 1 ); |
| if ( ( protocolIndex != -1 ) && ( pathIndex != -1 ) ) |
| { |
| hostname = jazzUrl.substring( protocolIndex, pathIndex ); |
| } |
| else |
| { |
| throw new ScmRepositoryException( |
| "Jazz Url \"" + jazzUrl + "\" is not a valid URL. The Jazz Url syntax is " + JAZZ_URL_FORMAT ); |
| } |
| } |
| |
| getLogger().debug( "Creating JazzScmProviderRepository with the following values:" ); |
| getLogger().debug( "jazzUrl - " + jazzUrl ); |
| getLogger().debug( "username - " + username ); |
| getLogger().debug( "password - " + password ); |
| getLogger().debug( "hostname - " + hostname ); |
| getLogger().debug( "port - " + port ); |
| getLogger().debug( "repositoryWorkspace - " + repositoryWorkspace ); |
| |
| return new JazzScmProviderRepository( jazzUrl, username, password, hostname, port, repositoryWorkspace ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzAddCommand command = new JazzAddCommand(); |
| command.setLogger( getLogger() ); |
| return (AddScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected BranchScmResult branch( ScmProviderRepository repository, ScmFileSet fileSet, |
| CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzBranchCommand command = new JazzBranchCommand(); |
| command.setLogger( getLogger() ); |
| return (BranchScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzBlameCommand command = new JazzBlameCommand(); |
| command.setLogger( getLogger() ); |
| return (BlameScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet, |
| CommandParameters parameters ) |
| throws ScmException |
| { |
| // We need to call the status command first, so that we can get the details of the workspace. |
| // This is needed for the list changesets command. |
| // We could also 'trust' the value in the pom. |
| JazzStatusCommand statusCommand = new JazzStatusCommand(); |
| statusCommand.setLogger( getLogger() ); |
| statusCommand.execute( repository, fileSet, parameters ); |
| |
| JazzChangeLogCommand command = new JazzChangeLogCommand(); |
| command.setLogger( getLogger() ); |
| return (ChangeLogScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet, |
| CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzCheckInCommand command = new JazzCheckInCommand(); |
| command.setLogger( getLogger() ); |
| return (CheckInScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet, |
| CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzCheckOutCommand command = new JazzCheckOutCommand(); |
| command.setLogger( getLogger() ); |
| return (CheckOutScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected DiffScmResult diff( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzDiffCommand command = new JazzDiffCommand(); |
| command.setLogger( getLogger() ); |
| return (DiffScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected EditScmResult edit( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzEditCommand command = new JazzEditCommand(); |
| command.setLogger( getLogger() ); |
| return (EditScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet, |
| CommandParameters parameters ) |
| throws ScmException |
| { |
| // Use checkout instead |
| return super.export( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected ListScmResult list( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) |
| throws ScmException |
| { |
| // We need to call the status command first, so that we can get the details of the stream etc. |
| // This is needed for workspace deliveries and snapshot promotions. |
| JazzStatusCommand statusCommand = new JazzStatusCommand(); |
| statusCommand.setLogger( getLogger() ); |
| statusCommand.execute( repository, fileSet, parameters ); |
| |
| JazzListCommand command = new JazzListCommand(); |
| command.setLogger( getLogger() ); |
| return (ListScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, |
| CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzStatusCommand command = new JazzStatusCommand(); |
| command.setLogger( getLogger() ); |
| return (StatusScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) |
| throws ScmException |
| { |
| // We need to call the status command first, so that we can get the details of the stream etc. |
| // This is needed for workspace deliveries and snapshot promotions. |
| JazzStatusCommand statusCommand = new JazzStatusCommand(); |
| statusCommand.setLogger( getLogger() ); |
| statusCommand.execute( repository, fileSet, parameters ); |
| |
| JazzTagCommand command = new JazzTagCommand(); |
| command.setLogger( getLogger() ); |
| return (TagScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet, |
| CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzUpdateCommand command = new JazzUpdateCommand(); |
| command.setLogger( getLogger() ); |
| return (UpdateScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected UnEditScmResult unedit( ScmProviderRepository repository, ScmFileSet fileSet, |
| CommandParameters parameters ) |
| throws ScmException |
| { |
| JazzUnEditCommand command = new JazzUnEditCommand(); |
| command.setLogger( getLogger() ); |
| return (UnEditScmResult) command.execute( repository, fileSet, parameters ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getScmSpecificFilename() |
| { |
| return JazzConstants.SCM_META_DATA_FOLDER; |
| } |
| |
| } |