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.BuildDefinitionDao;
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.Project;
30  import org.apache.maven.continuum.model.scm.ScmResult;
31  import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
32  import org.apache.maven.continuum.project.ContinuumProjectState;
33  import org.apache.maven.continuum.store.ContinuumStoreException;
34  import org.apache.maven.scm.ScmException;
35  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
36  import org.apache.maven.scm.manager.NoSuchScmProviderException;
37  import org.apache.maven.scm.repository.ScmRepositoryException;
38  import org.codehaus.plexus.util.StringUtils;
39  
40  import java.io.File;
41  import java.util.Iterator;
42  import java.util.List;
43  import java.util.Map;
44  
45  /**
46   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
47   * @version $Id: CheckoutProjectContinuumAction.java 1372260 2012-08-13 04:29:09Z brett $
48   * @plexus.component role="org.codehaus.plexus.action.Action" role-hint="checkout-project"
49   */
50  public class CheckoutProjectContinuumAction
51      extends AbstractContinuumAction
52  {
53      private static final String KEY_SCM_USERNAME = "scmUserName";
54  
55      private static final String KEY_SCM_PASSWORD = "scmUserPassword";
56  
57      private static final String KEY_PROJECT_RELATIVE_PATH = "project-relative-path";
58  
59      private static final String KEY_CHECKOUT_SCM_RESULT = "checkout-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 BuildDefinitionDao buildDefinitionDao;
75  
76      /**
77       * @plexus.requirement
78       */
79      private ProjectDao projectDao;
80  
81      public void execute( Map context )
82          throws ContinuumStoreException
83      {
84          Project project = projectDao.getProject( getProject( context ).getId() );
85  
86          BuildDefinition buildDefinition = getBuildDefinition( context );
87  
88          if ( buildDefinition != null )
89          {
90              buildDefinition = buildDefinitionDao.getBuildDefinition( buildDefinition.getId() );
91          }
92  
93          int originalState = project.getState();
94  
95          project.setState( ContinuumProjectState.CHECKING_OUT );
96  
97          projectDao.updateProject( project );
98  
99          File workingDirectory = getWorkingDirectory( context );
100 
101         // ----------------------------------------------------------------------
102         // Check out the project
103         // ----------------------------------------------------------------------
104 
105         ScmResult result;
106 
107         List<Project> projectsWithCommonScmRoot = getListOfProjectsInGroupWithCommonScmRoot( context );
108 
109         try
110         {
111             String scmUserName = getScmUsername( context, project.getScmUsername() );
112             String scmPassword = getScmPassword( context, project.getScmPassword() );
113             String scmRootUrl = getProjectScmRootUrl( context, project.getScmUrl() );
114 
115             ContinuumScmConfiguration config = createScmConfiguration( project, workingDirectory, scmUserName,
116                                                                        scmPassword, scmRootUrl, isRootDirectory(
117                 context ) );
118 
119             String tag = config.getTag();
120             getLogger().info(
121                 "Checking out project: '" + project.getName() + "', id: '" + project.getId() + "' " + "to '" +
122                     workingDirectory + "'" + ( tag != null ? " with branch/tag " + tag + "." : "." ) );
123 
124             CheckOutScmResult checkoutResult = scm.checkout( config );
125             if ( StringUtils.isNotEmpty( checkoutResult.getRelativePathProjectDirectory() ) )
126             {
127                 setProjectRelativePath( context, checkoutResult.getRelativePathProjectDirectory() );
128             }
129 
130             if ( !checkoutResult.isSuccess() )
131             {
132                 // TODO: is it more appropriate to return this in the converted result so that it can be presented to
133                 // the user?
134                 String msg = "Error while checking out the code for project: '" + project.getName() + "', id: '" +
135                     project.getId() + "' to '" + workingDirectory.getAbsolutePath() + "'" +
136                     ( tag != null ? " with branch/tag " + tag + "." : "." );
137                 getLogger().warn( msg );
138 
139                 getLogger().warn( "Command output: " + checkoutResult.getCommandOutput() );
140 
141                 getLogger().warn( "Provider message: " + checkoutResult.getProviderMessage() );
142             }
143             else
144             {
145                 getLogger().info( "Checked out " + checkoutResult.getCheckedOutFiles().size() + " files." );
146             }
147 
148             result = convertScmResult( checkoutResult );
149         }
150         catch ( ScmRepositoryException e )
151         {
152             result = new ScmResult();
153 
154             result.setSuccess( false );
155 
156             result.setProviderMessage( e.getMessage() + ": " + getValidationMessages( e ) );
157 
158             getLogger().error( e.getMessage(), e );
159         }
160         catch ( NoSuchScmProviderException e )
161         {
162             // TODO: this is not making it back into a result of any kind - log it at least. Same is probably the case for ScmException
163             result = new ScmResult();
164 
165             result.setSuccess( false );
166 
167             result.setProviderMessage( e.getMessage() );
168 
169             getLogger().error( e.getMessage(), e );
170         }
171         catch ( ScmException e )
172         {
173             result = new ScmResult();
174 
175             result.setSuccess( false );
176 
177             result.setException( ContinuumUtils.throwableMessagesToString( e ) );
178 
179             getLogger().error( e.getMessage(), e );
180         }
181         catch ( Throwable t )
182         {
183             // TODO: do we want this here, or should it be to the logs?
184             // TODO: what throwables do we really get here that we can cope with?
185             result = new ScmResult();
186 
187             result.setSuccess( false );
188 
189             result.setException( ContinuumUtils.throwableMessagesToString( t ) );
190 
191             getLogger().error( t.getMessage(), t );
192         }
193         finally
194         {
195             String relativePath = getProjectRelativePath( context );
196 
197             if ( StringUtils.isNotEmpty( relativePath ) )
198             {
199                 project.setRelativePath( relativePath );
200             }
201 
202             project = projectDao.getProject( project.getId() );
203 
204             if ( originalState == ContinuumProjectState.NEW )
205             {
206                 project.setState( ContinuumProjectState.CHECKEDOUT );
207             }
208             else
209             {
210                 project.setState( originalState );
211             }
212 
213             projectDao.updateProject( project );
214 
215             // update state of sub-projects 
216             // if multi-module project was checked out in a single directory, these must not be null            
217             for ( Project projectWithCommonScmRoot : projectsWithCommonScmRoot )
218             {
219                 projectWithCommonScmRoot = projectDao.getProject( projectWithCommonScmRoot.getId() );
220                 if ( projectWithCommonScmRoot != null && projectWithCommonScmRoot.getId() != project.getId() &&
221                     projectWithCommonScmRoot.getState() == ContinuumProjectState.NEW )
222                 {
223                     projectWithCommonScmRoot.setState( ContinuumProjectState.CHECKEDOUT );
224                     projectDao.updateProject( projectWithCommonScmRoot );
225                 }
226             }
227 
228             notifier.checkoutComplete( project, buildDefinition );
229         }
230 
231         setCheckoutScmResult( context, result );
232         setProject( context, project );
233     }
234 
235     private ContinuumScmConfiguration createScmConfiguration( Project project, File workingDirectory,
236                                                               String scmUserName, String scmPassword, String scmRootUrl,
237                                                               boolean isRootDirectory )
238     {
239         ContinuumScmConfiguration config = new ContinuumScmConfiguration();
240 
241         if ( project.isCheckedOutInSingleDirectory() && scmRootUrl != null && !"".equals( scmRootUrl ) &&
242             isRootDirectory )
243         {
244             config.setUrl( scmRootUrl );
245         }
246         else
247         {
248             config.setUrl( project.getScmUrl() );
249         }
250 
251         // CONTINUUM-2628
252         config = ContinuumScmUtils.setSCMCredentialsforSSH( config, config.getUrl(), scmUserName, scmPassword );
253 
254         config.setUseCredentialsCache( project.isScmUseCache() );
255         config.setWorkingDirectory( workingDirectory );
256         config.setTag( project.getScmTag() );
257         return config;
258     }
259 
260     private ScmResult convertScmResult( CheckOutScmResult scmResult )
261     {
262         ScmResult result = new ScmResult();
263 
264         result.setSuccess( scmResult.isSuccess() );
265 
266         result.setCommandLine( maskPassword( scmResult.getCommandLine() ) );
267 
268         result.setCommandOutput( scmResult.getCommandOutput() );
269 
270         result.setProviderMessage( scmResult.getProviderMessage() );
271 
272         return result;
273     }
274 
275     // TODO: migrate to the SvnCommandLineUtils version (preferably properly encapsulated in the provider)
276     private String maskPassword( String commandLine )
277     {
278         String cmd = commandLine;
279 
280         if ( cmd != null && cmd.startsWith( "svn" ) )
281         {
282             String pwdString = "--password";
283 
284             if ( cmd.indexOf( pwdString ) > 0 )
285             {
286                 int index = cmd.indexOf( pwdString ) + pwdString.length() + 1;
287 
288                 int nextSpace = cmd.indexOf( " ", index );
289 
290                 cmd = cmd.substring( 0, index ) + "********" + cmd.substring( nextSpace );
291             }
292         }
293 
294         return cmd;
295     }
296 
297     private String getValidationMessages( ScmRepositoryException ex )
298     {
299         List<String> messages = ex.getValidationMessages();
300 
301         StringBuffer message = new StringBuffer();
302 
303         if ( messages != null && !messages.isEmpty() )
304         {
305             for ( Iterator<String> i = messages.iterator(); i.hasNext(); )
306             {
307                 message.append( i.next() );
308 
309                 if ( i.hasNext() )
310                 {
311                     message.append( System.getProperty( "line.separator" ) );
312                 }
313             }
314         }
315         return message.toString();
316     }
317 
318     public static String getScmUsername( Map<String, Object> context, String defaultValue )
319     {
320         return getString( context, KEY_SCM_USERNAME, defaultValue );
321     }
322 
323     public static void setScmUsername( Map<String, Object> context, String scmUsername )
324     {
325         context.put( KEY_SCM_USERNAME, scmUsername );
326     }
327 
328     public static String getScmPassword( Map<String, Object> context, String defaultValue )
329     {
330         return getString( context, KEY_SCM_PASSWORD, defaultValue );
331     }
332 
333     public static void setScmPassword( Map<String, Object> context, String scmPassword )
334     {
335         context.put( KEY_SCM_PASSWORD, scmPassword );
336     }
337 
338     public static String getProjectRelativePath( Map<String, Object> context )
339     {
340         return getString( context, KEY_PROJECT_RELATIVE_PATH, "" );
341     }
342 
343     public static void setProjectRelativePath( Map<String, Object> context, String projectRelativePath )
344     {
345         context.put( KEY_PROJECT_RELATIVE_PATH, projectRelativePath );
346     }
347 
348     public static ScmResult getCheckoutScmResult( Map<String, Object> context, ScmResult defaultValue )
349     {
350         return (ScmResult) getObject( context, KEY_CHECKOUT_SCM_RESULT, defaultValue );
351     }
352 
353     public static void setCheckoutScmResult( Map<String, Object> context, ScmResult scmResult )
354     {
355         context.put( KEY_CHECKOUT_SCM_RESULT, scmResult );
356     }
357 }