View Javadoc
1   package org.apache.maven.scm.provider.jazz.command;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.scm.ScmException;
23  import org.apache.maven.scm.ScmFileSet;
24  import org.apache.maven.scm.log.ScmLogger;
25  import org.apache.maven.scm.provider.ScmProviderRepository;
26  import org.apache.maven.scm.provider.jazz.command.consumer.ErrorConsumer;
27  import org.apache.maven.scm.provider.jazz.repository.JazzScmProviderRepository;
28  import org.codehaus.plexus.util.Os;
29  import org.codehaus.plexus.util.StringUtils;
30  import org.codehaus.plexus.util.cli.CommandLineException;
31  import org.codehaus.plexus.util.cli.CommandLineUtils;
32  import org.codehaus.plexus.util.cli.Commandline;
33  import org.codehaus.plexus.util.cli.StreamConsumer;
34  
35  import java.io.File;
36  import java.util.Iterator;
37  
38  /**
39   * The base class for the underlying jazz "scm.sh"/"scm.exe" command.
40   * <p/>
41   * The SCM command is documented here:
42   * <p/>
43   * V2.0.2: http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_scm.html
44   * V3.0:   http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_scm.html
45   * V3.0.1: http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_scm.html
46   *
47   * @author <a href="mailto:ChrisGWarp@gmail.com">Chris Graham</a>
48   */
49  public class JazzScmCommand
50  {
51      // The logger to use.
52      private ScmLogger fLogger;
53  
54      // The Commandline that we build up and execute.
55      private Commandline fCommand;
56  
57      /**
58       * Create a JazzScmCommand when no sub-command is needed.
59       *
60       * @throws ScmException
61       */
62      public JazzScmCommand( String cmd, ScmProviderRepository repo, ScmFileSet fileSet, ScmLogger logger )
63      {
64          this( cmd, null, repo, true, fileSet, logger );
65      }
66  
67      /**
68       * Create a JazzScmCommand when a sub-command is needed.
69       * eg: "create snapshot ..."
70       */
71      public JazzScmCommand( String cmd, String subCmd, ScmProviderRepository repo, ScmFileSet fileSet, ScmLogger logger )
72      {
73          this( cmd, subCmd, repo, true, fileSet, logger );
74      }
75  
76      /**
77       * Create a JazzScmCommand, adding the repository-uri as needed.
78       */
79      public JazzScmCommand( String cmd, String subCmd, ScmProviderRepository repo, boolean addRepositoryWorkspaceArg,
80                             ScmFileSet fileSet, ScmLogger logger )
81      {
82          fLogger = logger;
83          fCommand = new Commandline();
84  
85          // TODO This was developed and tested in Windows (in which scm (scm.exe) was the valid executable)
86          // Verify that the executable is valid in other operating systems.
87          fCommand.setExecutable( JazzConstants.SCM_EXECUTABLE );
88  
89          if ( fileSet != null )
90          {
91              fCommand.setWorkingDirectory( fileSet.getBasedir().getAbsolutePath() );
92  
93              // Make the directory, if need be.
94              if ( !fCommand.getWorkingDirectory().exists() )
95              {
96                  boolean success = fCommand.getWorkingDirectory().mkdirs();
97                  if ( !success )
98                  {
99                      // Just log the error, don't throw an error, as it is going to fail anyway.
100                     logErrorMessage( "Working directory did not exist" + " and it couldn't be created: "
101                                          + fCommand.getWorkingDirectory() );
102                 }
103             }
104         }
105 
106         // Add the main command
107         if ( !StringUtils.isEmpty( cmd ) )
108         {
109             addArgument( cmd );
110         }
111 
112         // Add the sub-command if present
113         if ( !StringUtils.isEmpty( subCmd ) )
114         {
115             addArgument( subCmd );
116         }
117 
118         JazzScmProviderRepository jazzRepo = (JazzScmProviderRepository) repo;
119 
120         // Add the repository argument if needed (most commands need this, but not all)
121         if ( addRepositoryWorkspaceArg )
122         {
123             String repositoryWorkspace = jazzRepo.getRepositoryURI();
124             if ( !StringUtils.isEmpty( repositoryWorkspace ) )
125             {
126                 addArgument( JazzConstants.ARG_REPOSITORY_URI );
127                 addArgument( jazzRepo.getRepositoryURI() );
128             }
129         }
130 
131         // Add the username argument
132         // TODO Figure out how we would use the login command / username caching so this is not required on each
133         // command.
134         String user = jazzRepo.getUser();
135         if ( !StringUtils.isEmpty( user ) )
136         {
137             addArgument( JazzConstants.ARG_USER_NAME );
138             addArgument( jazzRepo.getUser() );
139         }
140 
141         // Add the password argument
142         // TODO Figure out how we would use the login command / password caching so this is not required on each
143         // command.
144         String password = jazzRepo.getPassword();
145         if ( !StringUtils.isEmpty( password ) )
146         {
147             addArgument( JazzConstants.ARG_USER_PASSWORD );
148             addArgument( jazzRepo.getPassword() );
149         }
150     }
151 
152     public void addArgument( ScmFileSet fileSet )
153     {
154         logInfoMessage( "files: " + fileSet.getBasedir().getAbsolutePath() );
155         Iterator<File> iter = fileSet.getFileList().iterator();
156         while ( iter.hasNext() )
157         {
158             fCommand.createArg().setValue(  iter.next().getPath() );
159         }
160     }
161 
162     public void addArgument( String arg )
163     {
164         fCommand.createArg().setValue( arg );
165     }
166 
167     public int execute( StreamConsumer out, ErrorConsumer err )
168         throws ScmException
169     {
170         logInfoMessage( "Executing: " + cryptPassword( fCommand ) );
171         if ( fCommand.getWorkingDirectory() != null )
172         {
173             logInfoMessage( "Working directory: " + fCommand.getWorkingDirectory().getAbsolutePath() );
174         }
175 
176         int status = 0;
177         try
178         {
179             status = CommandLineUtils.executeCommandLine( fCommand, out, err );
180         }
181         catch ( CommandLineException e )
182         {
183             String errorOutput = err.getOutput();
184             if ( errorOutput.length() > 0 )
185             {
186                 logErrorMessage( "Error: " + err.getOutput() );
187             }
188             throw new ScmException( "Error while executing Jazz SCM command line - " + getCommandString(), e );
189         }
190         String errorOutput = err.getOutput();
191         if ( errorOutput.length() > 0 )
192         {
193             logErrorMessage( "Error: " + err.getOutput() );
194         }
195         return status;
196     }
197 
198     public String getCommandString()
199     {
200         return fCommand.toString();
201     }
202 
203     public Commandline getCommandline()
204     {
205         return fCommand;
206     }
207 
208     private void logErrorMessage( String message )
209     {
210         if ( fLogger != null )
211         {
212             fLogger.error( message );
213         }
214     }
215 
216     private void logInfoMessage( String message )
217     {
218         if ( fLogger != null )
219         {
220             fLogger.info( message );
221         }
222     }
223 
224     private void logDebugMessage( String message )
225     {
226         if ( fLogger != null )
227         {
228             fLogger.debug( message );
229         }
230     }
231 
232     // Unashamedly 'borrowed' from SvnCommandLineUtils
233     // (but fixed for cases where the line ends in the password (no trailing space or further input).
234     public static String cryptPassword( Commandline cl )
235     {
236         String clString = cl.toString();
237 
238         int pos = clString.indexOf( "--password" );
239 
240         if ( pos > 0 )
241         {
242             String beforePassword = clString.substring( 0, pos + "--password ".length() );
243             String afterPassword = clString.substring( pos + "--password ".length() );
244             pos = afterPassword.indexOf( ' ' );
245             if ( pos > 0 )
246             {
247                 afterPassword = afterPassword.substring( pos );
248             }
249             else
250             {
251                 afterPassword = "\"";
252             }
253             if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
254             {
255                 clString = beforePassword + "*****" + afterPassword;
256             }
257             else
258             {
259                 clString = beforePassword + "'*****'";
260             }
261         }
262 
263         return clString;
264     }
265 
266 }