View Javadoc
1   package org.apache.maven.scm.provider.jazz.command.checkin;
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 java.io.File;
23  import java.util.List;
24  
25  import org.apache.maven.scm.ScmException;
26  import org.apache.maven.scm.ScmFileSet;
27  import org.apache.maven.scm.ScmVersion;
28  import org.apache.maven.scm.command.add.AddScmResult;
29  import org.apache.maven.scm.command.checkin.AbstractCheckInCommand;
30  import org.apache.maven.scm.command.checkin.CheckInScmResult;
31  import org.apache.maven.scm.provider.ScmProviderRepository;
32  import org.apache.maven.scm.provider.jazz.command.JazzConstants;
33  import org.apache.maven.scm.provider.jazz.command.JazzScmCommand;
34  import org.apache.maven.scm.provider.jazz.command.add.JazzAddCommand;
35  import org.apache.maven.scm.provider.jazz.command.consumer.DebugLoggerConsumer;
36  import org.apache.maven.scm.provider.jazz.command.consumer.ErrorConsumer;
37  import org.apache.maven.scm.provider.jazz.command.status.JazzStatusCommand;
38  import org.apache.maven.scm.provider.jazz.repository.JazzScmProviderRepository;
39  import org.codehaus.plexus.util.StringUtils;
40  import org.codehaus.plexus.util.cli.StreamConsumer;
41  
42  // The Maven SCM Plugin "checkin" goal is equivalent to the RTC "checkin" command.
43  //
44  // This implementation of the Maven SCM Plugin "checkin" goal creates a change set with the message provided.
45  // It then uses the Jazz "scm "checkin" command to check the files into a remote workspace.
46  // If there is a flow target defined and the pushChanges flag is true (the default), then the remote workspace
47  // will be delivered ("scm deliver") to the flow target (a stream or other workspace).
48  // 
49  // Set the pushChanges flag to false, if you do not want the repository workspace delivered.
50  //
51  // NOTE: At this point, only a SINGLE flow target is supported. Jazz itself, allows for more than one.
52  //
53  // The differences between this and the "add" goal, are:
54  //      - The add goal will only checkin into the remote repository workspace.
55  //      - The add goal will never deliver.
56  //      - The add goal does not create a change set.
57  //
58  // This is the best we can do to mimic the implementations of the other providers, that provide a working
59  // "add" function (eg "svn add").
60  //
61  // Add may have had been able to use the "scm share" command, but that is recusive and only takes directory
62  // names; we are not able to specify specific or single files.
63  //
64  // See the following links for additional information on the RTC "checkin" command.
65  // RTC 2.0.0.2:
66  // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_checkin.html
67  // RTC 3.0:
68  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_checkin.html
69  // RTC 3.0.1:
70  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_checkin.html
71  //
72  // See the following links for additional information on the RTC "deliver" command.
73  // RTC 2.0.0.2:
74  // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_deliver.html
75  // RTC 3.0:
76  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_deliver.html
77  // RTC 3.0.1:
78  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_deliver.html
79  //
80  
81  /**
82   * @author <a href="mailto:ChrisGWarp@gmail.com">Chris Graham</a>
83   */
84  public class JazzCheckInCommand
85      extends AbstractCheckInCommand
86  {
87  
88      /**
89       * {@inheritDoc}
90       */
91      protected CheckInScmResult executeCheckInCommand( ScmProviderRepository repository, ScmFileSet fileSet,
92                                                        String message, ScmVersion scmVersion )
93          throws ScmException
94      {
95          if ( scmVersion != null && StringUtils.isNotEmpty( scmVersion.getName() ) )
96          {
97              throw new ScmException( "This provider command can't handle tags." );
98          }
99  
100         if ( getLogger().isDebugEnabled() )
101         {
102             getLogger().debug( "Executing checkin command..." );
103         }
104 
105         // Create a changeset. We need to do this, as otherwise the information contained in the message
106         // will be lost forever.
107         JazzScmCommand createChangesetCmd = createCreateChangesetCommand( repository, fileSet, message );
108         DebugLoggerConsumer outputConsumer = new DebugLoggerConsumer( getLogger() );
109         ErrorConsumer errConsumer = new ErrorConsumer( getLogger() );
110 
111         int status = createChangesetCmd.execute( outputConsumer, errConsumer );
112         if ( status != 0 )
113         {
114             return new CheckInScmResult( createChangesetCmd.getCommandString(),
115                                          "Error code for Jazz SCM create changeset command - " + status,
116                                          errConsumer.getOutput(), false );
117         }
118 
119         // As we just created a change set, we now need to call the status command so we can parse the 
120         // newly created change set.
121 
122         JazzStatusCommand statusCommand = new JazzStatusCommand();
123         statusCommand.setLogger( getLogger() );
124         statusCommand.executeStatusCommand( repository, fileSet );
125 
126         // NOTE: For isPushChangesAndHaveFlowTargets() to work, a scm status call must have been called first!!!
127         // As the Workspace name and alias, and the Flow Target name and alias are needed.
128         
129         // Check to see if we've got a flow target and had a workItem defined (via -DworkItem=XXXX)
130         JazzScmProviderRepository jazzRepo = (JazzScmProviderRepository) repository;
131         if ( jazzRepo.isPushChangesAndHaveFlowTargets() && StringUtils.isNotEmpty( jazzRepo.getWorkItem() ) )
132         {
133             List<Integer> changeSetAliases = jazzRepo.getOutgoingChangeSetAliases();
134             if ( changeSetAliases != null && !changeSetAliases.isEmpty() )
135             {
136                 for ( Integer changeSetAlias : changeSetAliases )
137                 {
138                     // Associate a work item if we need too.
139                     JazzScmCommand changesetAssociateCmd = createChangesetAssociateCommand( repository, 
140                         changeSetAlias );
141                     outputConsumer = new DebugLoggerConsumer( getLogger() );
142                     errConsumer = new ErrorConsumer( getLogger() );
143         
144                     status = changesetAssociateCmd.execute( outputConsumer, errConsumer );
145                     if ( status != 0 )
146                     {
147                         return new CheckInScmResult( changesetAssociateCmd.getCommandString(),
148                                                      "Error code for Jazz SCM changeset associate command - " + status,
149                                                      errConsumer.getOutput(), false );
150                     }
151                 }
152             }
153         }
154         
155         // Now check in the files themselves.
156         return executeCheckInCommand( repository, fileSet, scmVersion );
157     }
158 
159     protected CheckInScmResult executeCheckInCommand( ScmProviderRepository repo, ScmFileSet fileSet,
160                                                       ScmVersion scmVersion )
161         throws ScmException
162     {
163         // Call the Add command to perform the checkin into the repository workspace.
164         JazzAddCommand addCommand = new JazzAddCommand();
165         addCommand.setLogger( getLogger() );
166         AddScmResult addResult = addCommand.executeAddCommand( repo, fileSet );
167 
168         // Now, if it has a flow target, deliver it.
169         JazzScmProviderRepository jazzRepo = (JazzScmProviderRepository) repo;
170         if ( jazzRepo.isPushChangesAndHaveFlowTargets() )
171         {
172             // Push if we need too
173             JazzScmCommand deliverCmd = createDeliverCommand( (JazzScmProviderRepository) repo, fileSet );
174             StreamConsumer deliverConsumer =
175                 new DebugLoggerConsumer( getLogger() );      // No need for a dedicated consumer for this
176             ErrorConsumer errConsumer = new ErrorConsumer( getLogger() );
177 
178             int status = deliverCmd.execute( deliverConsumer, errConsumer );
179             if ( status != 0 )
180             {
181                 return new CheckInScmResult( deliverCmd.getCommandString(),
182                                              "Error code for Jazz SCM deliver command - " + status,
183                                              errConsumer.getOutput(), false );
184             }
185         }
186 
187         // Return what was added.
188         return new CheckInScmResult( addResult.getCommandLine(), addResult.getAddedFiles() );
189     }
190 
191     public JazzScmCommand createCreateChangesetCommand( ScmProviderRepository repo, ScmFileSet fileSet, String message )
192     {
193         JazzScmCommand command =
194             new JazzScmCommand( JazzConstants.CMD_CREATE, JazzConstants.CMD_SUB_CHANGESET, repo, false, fileSet,
195                                 getLogger() );
196         command.addArgument( message );
197 
198         return command;
199     }
200 
201     public JazzScmCommand createChangesetAssociateCommand( ScmProviderRepository repo, Integer changeSetAlias )
202     {
203         JazzScmCommand command =
204             new JazzScmCommand( JazzConstants.CMD_CHANGESET, JazzConstants.CMD_SUB_ASSOCIATE, repo, false, null,
205                                 getLogger() );
206         // Add the change set alias
207         JazzScmProviderRepository jazzRepo = (JazzScmProviderRepository) repo;
208         // SCM-812 - Jazz SCM Alias Id's roll over to zero, not 1000 as advertised.
209         //           So, we need to add the changeSetAlias with leading zeros.
210         command.addArgument( StringUtils.leftPad( changeSetAlias.toString(), 4, "0" ) );
211         // Add the work item number
212         command.addArgument( jazzRepo.getWorkItem() );
213         return command;
214     }
215 
216     public JazzScmCommand createCheckInCommand( ScmProviderRepository repo, ScmFileSet fileSet )
217     {
218         JazzScmCommand command =
219             new JazzScmCommand( JazzConstants.CMD_CHECKIN, null, repo, false, fileSet, getLogger() );
220 
221         // TODO, this was taken out to quickly test how the release plugin works.
222         // The release plugin has the fileSet.getbaseDir() as the project it is checking in
223         // This happens to be a folder under the sandbox root, and so the checkin would fail because it needs
224         // to check in at the sandbox root level (not sub folders)
225         // The SCM Plugin has a basedir parameter that you can pass it, so everythig works ok from the scm-plugin alone
226         // but the release-plugin doesn't look like it lets you do that. (or I didn't have enough time
227         // to figure out how to do it properly).
228 
229         // if (fileSet != null) {
230         // command.addArgument(JazzConstants.LOAD_ROOT_DIRECTORY_ARG);
231         // command.addArgument(fileSet.getBasedir().getAbsolutePath());
232         // }
233 
234         List<File> files = fileSet.getFileList();
235         if ( files != null && !files.isEmpty() )
236         {
237             for ( File file : files )
238             {
239                 command.addArgument( file.getPath() ); // Check in only the files specified
240             }
241         }
242         else
243         {
244             command.addArgument( "." ); // This will check in all local changes
245         }
246 
247         return command;
248     }
249 
250     // Create the JazzScmCommand to execute the "scm deliver ..." command
251     // This will deliver the changes to the flow target (stream or other workspace).
252     public JazzScmCommand createDeliverCommand( JazzScmProviderRepository repo, ScmFileSet fileSet )
253     {
254         JazzScmCommand command = new JazzScmCommand( JazzConstants.CMD_DELIVER, repo, fileSet, getLogger() );
255 
256         if ( repo.getWorkspace() != null && !repo.getWorkspace().equals( "" ) )
257         {
258             command.addArgument( JazzConstants.ARG_DELIVER_SOURCE );
259             command.addArgument( repo.getWorkspace() );
260         }
261 
262         if ( repo.getFlowTarget() != null && !repo.getFlowTarget().equals( "" ) )
263         {
264             command.addArgument( JazzConstants.ARG_DELIVER_TARGET );
265             command.addArgument( repo.getFlowTarget() );
266         }
267 
268         // This command is needed so that the deliver operation will work.
269         // Files that are not under source control (a--) [temp files etc]
270         // will cause the deliver operation to fail with the error:
271         // "Cannot deliver because there are one or more items that are not checked in.
272         // Check in the changes or rerun with --overwrite-uncommitted."
273         // However, from the maven perspective, we only need files that are
274         // under source control to be delivered. Maven has already checked
275         // for this (via the status command). 
276         //
277         // So we add this argument to allow the deliver to work.
278         command.addArgument( JazzConstants.ARG_OVERWRITE_UNCOMMITTED );
279 
280         return command;
281     }
282 }