View Javadoc

1   package org.apache.maven.continuum.core.action;
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.continuum.dao.BuildResultDao;
23  import org.apache.continuum.dao.ProjectDao;
24  import org.apache.continuum.scm.ContinuumScm;
25  import org.apache.continuum.scm.ContinuumScmConfiguration;
26  import org.apache.continuum.scm.ContinuumScmUtils;
27  import org.apache.continuum.utils.ContinuumUtils;
28  import org.apache.maven.continuum.model.project.BuildDefinition;
29  import org.apache.maven.continuum.model.project.BuildResult;
30  import org.apache.maven.continuum.model.project.Project;
31  import org.apache.maven.continuum.model.scm.ChangeFile;
32  import org.apache.maven.continuum.model.scm.ChangeSet;
33  import org.apache.maven.continuum.model.scm.ScmResult;
34  import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
35  import org.apache.maven.continuum.project.ContinuumProjectState;
36  import org.apache.maven.continuum.store.ContinuumObjectNotFoundException;
37  import org.apache.maven.continuum.store.ContinuumStoreException;
38  import org.apache.maven.continuum.utils.WorkingDirectoryService;
39  import org.apache.maven.scm.ScmException;
40  import org.apache.maven.scm.ScmFile;
41  import org.apache.maven.scm.command.update.UpdateScmResult;
42  import org.apache.maven.scm.manager.NoSuchScmProviderException;
43  import org.apache.maven.scm.repository.ScmRepositoryException;
44  
45  import java.io.File;
46  import java.util.Date;
47  import java.util.Iterator;
48  import java.util.List;
49  import java.util.Map;
50  
51  /**
52   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
53   * @version $Id: UpdateWorkingDirectoryFromScmContinuumAction.java 1372260 2012-08-13 04:29:09Z brett $
54   * @plexus.component role="org.codehaus.plexus.action.Action" role-hint="update-working-directory-from-scm"
55   */
56  public class UpdateWorkingDirectoryFromScmContinuumAction
57      extends AbstractContinuumAction
58  {
59      private static final String KEY_UPDATE_SCM_RESULT = "update-result";
60  
61      /**
62       * @plexus.requirement
63       */
64      private ContinuumNotificationDispatcher notifier;
65  
66      /**
67       * @plexus.requirement
68       */
69      private ContinuumScm scm;
70  
71      /**
72       * @plexus.requirement
73       */
74      private WorkingDirectoryService workingDirectoryService;
75  
76      /**
77       * @plexus.requirement
78       */
79      private BuildResultDao buildResultDao;
80  
81      /**
82       * @plexus.requirement
83       */
84      private ProjectDao projectDao;
85  
86      public void execute( Map context )
87          throws ScmRepositoryException, NoSuchScmProviderException, ScmException, ContinuumObjectNotFoundException,
88          ContinuumStoreException
89      {
90          Project project = projectDao.getProject( getProject( context ).getId() );
91  
92          BuildDefinition buildDefinition = getBuildDefinition( context );
93  
94          UpdateScmResult scmResult;
95  
96          ScmResult result;
97  
98          Date latestUpdateDate = null;
99  
100         int originalState = project.getState();
101 
102         project.setState( ContinuumProjectState.UPDATING );
103 
104         projectDao.updateProject( project );
105 
106         try
107         {
108             BuildResult buildResult = buildResultDao.getLatestBuildResultForProject( project.getId() );
109 
110             latestUpdateDate = new Date( buildResult.getStartTime() );
111         }
112         catch ( Exception e )
113         {
114         }
115 
116         try
117         {
118             notifier.checkoutStarted( project, buildDefinition );
119 
120             List<Project> projectsWithCommonScmRoot = getListOfProjectsInGroupWithCommonScmRoot( context );
121             String projectScmRootUrl = getProjectScmRootUrl( context, project.getScmUrl() );
122 
123             // TODO: not sure why this is different to the context, but it all needs to change
124             File workingDirectory = workingDirectoryService.getWorkingDirectory( project, projectScmRootUrl,
125                                                                                  projectsWithCommonScmRoot );
126 
127             ContinuumScmConfiguration config = createScmConfiguration( project, workingDirectory, projectScmRootUrl );
128             config.setLatestUpdateDate( latestUpdateDate );
129             String tag = config.getTag();
130             String msg =
131                 project.getName() + "', id: '" + project.getId() + "' to '" + workingDirectory.getAbsolutePath() + "'" +
132                     ( tag != null ? " with branch/tag " + tag + "." : "." );
133             getLogger().info( "Updating project: " + msg );
134             scmResult = scm.update( config );
135 
136             if ( !scmResult.isSuccess() )
137             {
138                 getLogger().warn( "Error while updating the code for project: '" + msg );
139 
140                 getLogger().warn( "Command output: " + scmResult.getCommandOutput() );
141 
142                 getLogger().warn( "Provider message: " + scmResult.getProviderMessage() );
143             }
144 
145             if ( scmResult.getUpdatedFiles() != null && scmResult.getUpdatedFiles().size() > 0 )
146             {
147                 getLogger().info( "Updated " + scmResult.getUpdatedFiles().size() + " files." );
148             }
149 
150             result = convertScmResult( scmResult );
151         }
152         catch ( ScmRepositoryException e )
153         {
154             result = new ScmResult();
155 
156             result.setSuccess( false );
157 
158             result.setProviderMessage( e.getMessage() + ": " + getValidationMessages( e ) );
159 
160             getLogger().error( e.getMessage(), e );
161         }
162         catch ( NoSuchScmProviderException e )
163         {
164             // TODO: this is not making it back into a result of any kind - log it at least. Same is probably the case for ScmException
165             result = new ScmResult();
166 
167             result.setSuccess( false );
168 
169             result.setProviderMessage( e.getMessage() );
170 
171             getLogger().error( e.getMessage(), e );
172         }
173         catch ( ScmException e )
174         {
175             result = new ScmResult();
176 
177             result.setSuccess( false );
178 
179             result.setException( ContinuumUtils.throwableMessagesToString( e ) );
180 
181             getLogger().error( e.getMessage(), e );
182         }
183         finally
184         {
185             // set back to the original state
186             try
187             {
188                 project = projectDao.getProject( project.getId() );
189 
190                 project.setState( originalState );
191 
192                 projectDao.updateProject( project );
193             }
194             catch ( Exception e )
195             {
196                 // nasty nasty, but we're in finally, so just sacrifice the state to keep the original exception
197                 getLogger().error( e.getMessage(), e );
198             }
199 
200             notifier.checkoutComplete( project, buildDefinition );
201         }
202 
203         setUpdateScmResult( context, result );
204         AbstractContinuumAction.setProject( context, project );
205     }
206 
207     private ContinuumScmConfiguration createScmConfiguration( Project project, File workingDirectory,
208                                                               String scmRootUrl )
209     {
210         ContinuumScmConfiguration config = new ContinuumScmConfiguration();
211 
212         if ( project.isCheckedOutInSingleDirectory() && scmRootUrl != null && !"".equals( scmRootUrl ) )
213         {
214             config.setUrl( scmRootUrl );
215         }
216         else
217         {
218             config.setUrl( project.getScmUrl() );
219         }
220 
221         // CONTINUUM-2628
222         config = ContinuumScmUtils.setSCMCredentialsforSSH( config, config.getUrl(), project.getScmUsername(),
223                                                             project.getScmPassword() );
224 
225         config.setUseCredentialsCache( project.isScmUseCache() );
226         config.setWorkingDirectory( workingDirectory );
227         config.setTag( project.getScmTag() );
228         return config;
229     }
230 
231     private ScmResult convertScmResult( UpdateScmResult scmResult )
232     {
233         ScmResult result = new ScmResult();
234 
235         result.setCommandLine( maskPassword( scmResult.getCommandLine() ) );
236 
237         result.setSuccess( scmResult.isSuccess() );
238 
239         result.setCommandOutput( scmResult.getCommandOutput() );
240 
241         result.setProviderMessage( scmResult.getProviderMessage() );
242 
243         if ( scmResult.getChanges() != null && !scmResult.getChanges().isEmpty() )
244         {
245             for ( org.apache.maven.scm.ChangeSet scmChangeSet : (List<org.apache.maven.scm.ChangeSet>) scmResult.getChanges() )
246             {
247                 ChangeSet change = new ChangeSet();
248 
249                 change.setAuthor( scmChangeSet.getAuthor() );
250 
251                 change.setComment( scmChangeSet.getComment() );
252 
253                 if ( scmChangeSet.getDate() != null )
254                 {
255                     change.setDate( scmChangeSet.getDate().getTime() );
256                 }
257 
258                 if ( scmChangeSet.getFiles() != null )
259                 {
260                     for ( org.apache.maven.scm.ChangeFile f : (List<org.apache.maven.scm.ChangeFile>) scmChangeSet.getFiles() )
261                     {
262                         ChangeFile file = new ChangeFile();
263 
264                         file.setName( f.getName() );
265 
266                         file.setRevision( f.getRevision() );
267 
268                         change.addFile( file );
269                     }
270                 }
271 
272                 result.addChange( change );
273             }
274         }
275         else
276         {
277             // We don't have a changes information probably because provider doesn't have a changelog command
278             // so we use the updated list that contains only the updated files list
279             ChangeSet changeSet = convertScmFileSetToChangeSet( scmResult.getUpdatedFiles() );
280 
281             if ( changeSet != null )
282             {
283                 result.addChange( changeSet );
284             }
285 
286         }
287 
288         return result;
289     }
290 
291     private static ChangeSet convertScmFileSetToChangeSet( List<ScmFile> files )
292     {
293         ChangeSet changeSet = null;
294 
295         if ( files != null && !files.isEmpty() )
296         {
297             changeSet = new ChangeSet();
298 
299             // TODO: author, etc.
300             for ( ScmFile scmFile : files )
301             {
302                 ChangeFile file = new ChangeFile();
303 
304                 file.setName( scmFile.getPath() );
305 
306                 // TODO: revision?
307 
308                 file.setStatus( scmFile.getStatus().toString() );
309 
310                 changeSet.addFile( file );
311             }
312         }
313         return changeSet;
314     }
315 
316     // TODO: migrate to the SvnCommandLineUtils version (preferably properly encapsulated in the provider)
317     private String maskPassword( String commandLine )
318     {
319         String cmd = commandLine;
320 
321         if ( cmd != null && cmd.startsWith( "svn" ) )
322         {
323             String pwdString = "--password";
324 
325             if ( cmd.indexOf( pwdString ) > 0 )
326             {
327                 int index = cmd.indexOf( pwdString ) + pwdString.length() + 1;
328 
329                 int nextSpace = cmd.indexOf( " ", index );
330 
331                 cmd = cmd.substring( 0, index ) + "********" + cmd.substring( nextSpace );
332             }
333         }
334 
335         return cmd;
336     }
337 
338     private String getValidationMessages( ScmRepositoryException ex )
339     {
340         List<String> messages = ex.getValidationMessages();
341 
342         StringBuffer message = new StringBuffer();
343 
344         if ( messages != null && !messages.isEmpty() )
345         {
346             for ( Iterator<String> i = messages.iterator(); i.hasNext(); )
347             {
348                 message.append( i.next() );
349 
350                 if ( i.hasNext() )
351                 {
352                     message.append( System.getProperty( "line.separator" ) );
353                 }
354             }
355         }
356         return message.toString();
357     }
358 
359     public static ScmResult getUpdateScmResult( Map<String, Object> context, ScmResult defaultValue )
360     {
361         return (ScmResult) getObject( context, KEY_UPDATE_SCM_RESULT, defaultValue );
362     }
363 
364     public static void setUpdateScmResult( Map<String, Object> context, ScmResult scmResult )
365     {
366         context.put( KEY_UPDATE_SCM_RESULT, scmResult );
367     }
368 }