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