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