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ø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 | } |