001 package org.apache.maven.scm.plugin;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.io.File;
023 import java.io.IOException;
024 import java.util.ArrayList;
025 import java.util.Iterator;
026 import java.util.List;
027 import java.util.Map;
028 import java.util.Map.Entry;
029 import java.util.Properties;
030
031 import org.apache.maven.plugin.AbstractMojo;
032 import org.apache.maven.plugin.MojoExecutionException;
033 import org.apache.maven.scm.ScmBranch;
034 import org.apache.maven.scm.ScmException;
035 import org.apache.maven.scm.ScmFileSet;
036 import org.apache.maven.scm.ScmResult;
037 import org.apache.maven.scm.ScmRevision;
038 import org.apache.maven.scm.ScmTag;
039 import org.apache.maven.scm.ScmVersion;
040 import org.apache.maven.scm.manager.ScmManager;
041 import org.apache.maven.scm.provider.ScmProviderRepository;
042 import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
043 import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
044 import org.apache.maven.scm.repository.ScmRepository;
045 import org.apache.maven.scm.repository.ScmRepositoryException;
046 import org.apache.maven.settings.Server;
047 import org.apache.maven.settings.Settings;
048 import org.apache.maven.shared.model.fileset.FileSet;
049 import org.apache.maven.shared.model.fileset.util.FileSetManager;
050 import org.codehaus.plexus.util.StringUtils;
051 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
052 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException;
053
054 /**
055 * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
056 * @author Olivier Lamy
057 *
058 */
059 public abstract class AbstractScmMojo
060 extends AbstractMojo
061 {
062 /**
063 * The SCM connection URL.
064 *
065 * @parameter expression="${connectionUrl}" default-value="${project.scm.connection}"
066 */
067 private String connectionUrl;
068
069 /**
070 * The SCM connection URL for developers.
071 *
072 * @parameter expression="${connectionUrl}" default-value="${project.scm.developerConnection}"
073 */
074 private String developerConnectionUrl;
075
076 /**
077 * The type of connection to use (connection or developerConnection).
078 *
079 * @parameter expression="${connectionType}" default-value="connection"
080 */
081 private String connectionType;
082
083 /**
084 * The working directory.
085 *
086 * @parameter expression="${workingDirectory}"
087 */
088 private File workingDirectory;
089
090 /**
091 * The user name (used by svn, starteam and perforce protocol).
092 *
093 * @parameter expression="${username}"
094 */
095 private String username;
096
097 /**
098 * The user password (used by svn, starteam and perforce protocol).
099 *
100 * @parameter expression="${password}"
101 */
102 private String password;
103
104 /**
105 * The private key (used by java svn).
106 *
107 * @parameter expression="${privateKey}"
108 */
109 private String privateKey;
110
111 /**
112 * The passphrase (used by java svn).
113 *
114 * @parameter expression="${passphrase}"
115 */
116 private String passphrase;
117
118 /**
119 * The url of tags base directory (used by svn protocol). It is not
120 * necessary to set it if you use the standard svn layout
121 * (branches/tags/trunk).
122 *
123 * @parameter expression="${tagBase}"
124 */
125 private String tagBase;
126
127 /**
128 * Comma separated list of includes file pattern.
129 *
130 * @parameter expression="${includes}"
131 */
132 private String includes;
133
134 /**
135 * Comma separated list of excludes file pattern.
136 *
137 * @parameter expression="${excludes}"
138 */
139 private String excludes;
140
141 /**
142 * @component
143 */
144 private ScmManager manager;
145
146 /**
147 * When this plugin requires Maven 3.0 as minimum, this component can be removed and o.a.m.s.c.SettingsDecrypter be
148 * used instead.
149 *
150 * @component roleHint="mng-4384"
151 */
152 private SecDispatcher secDispatcher;
153
154 /**
155 * The base directory.
156 *
157 * @parameter expression="${basedir}"
158 * @required
159 */
160 private File basedir;
161
162 /**
163 * @parameter default-value="${settings}"
164 * @required
165 * @readonly
166 */
167 private Settings settings;
168
169 /**
170 * List of System properties to pass to the JUnit tests.
171 *
172 * @parameter
173 */
174 private Properties systemProperties;
175
176 /**
177 * List of provider implementations.
178 *
179 * @parameter
180 */
181 private Map<String,String> providerImplementations;
182
183 /**
184 * Should distributed changes be pushed to the central repository?
185 * For many distributed SCMs like Git, a change like a commit
186 * is only stored in your local copy of the repository. Pushing
187 * the change allows your to more easily share it with other users.
188 *
189 * @parameter expression="${pushChanges}" default-value="true"
190 * @since 1.4
191 */
192 private boolean pushChanges;
193
194 /** {@inheritDoc} */
195 public void execute()
196 throws MojoExecutionException
197 {
198 if ( systemProperties != null )
199 {
200 // Add all system properties configured by the user
201 Iterator<Object> iter = systemProperties.keySet().iterator();
202
203 while ( iter.hasNext() )
204 {
205 String key = (String) iter.next();
206
207 String value = systemProperties.getProperty( key );
208
209 System.setProperty( key, value );
210 }
211 }
212
213 if ( providerImplementations != null && !providerImplementations.isEmpty() )
214 {
215 for ( Entry<String,String> entry : providerImplementations.entrySet() )
216 {
217 String providerType = entry.getKey();
218 String providerImplementation = entry.getValue();
219 getLog().info(
220 "Change the default '" + providerType + "' provider implementation to '"
221 + providerImplementation + "'." );
222 getScmManager().setScmProviderImplementation( providerType, providerImplementation );
223 }
224 }
225 }
226
227 protected void setConnectionType( String connectionType )
228 {
229 this.connectionType = connectionType;
230 }
231
232 public String getConnectionUrl()
233 {
234 boolean requireDeveloperConnection = !"connection".equals( connectionType.toLowerCase() );
235 if ( StringUtils.isNotEmpty( connectionUrl ) && !requireDeveloperConnection )
236 {
237 return connectionUrl;
238 }
239 else if ( StringUtils.isNotEmpty( developerConnectionUrl ) )
240 {
241 return developerConnectionUrl;
242 }
243 if ( requireDeveloperConnection )
244 {
245 throw new NullPointerException( "You need to define a developerConnectionUrl parameter" );
246 }
247 else
248 {
249 throw new NullPointerException( "You need to define a connectionUrl parameter" );
250 }
251 }
252
253 public void setConnectionUrl( String connectionUrl )
254 {
255 this.connectionUrl = connectionUrl;
256 }
257
258 public File getWorkingDirectory()
259 {
260 if ( workingDirectory == null )
261 {
262 return basedir;
263 }
264
265 return workingDirectory;
266 }
267
268 public void setWorkingDirectory( File workingDirectory )
269 {
270 this.workingDirectory = workingDirectory;
271 }
272
273 public ScmManager getScmManager()
274 {
275 return manager;
276 }
277
278 public ScmFileSet getFileSet()
279 throws IOException
280 {
281 if ( includes != null || excludes != null )
282 {
283 return new ScmFileSet( getWorkingDirectory(), includes, excludes );
284 }
285 else
286 {
287 return new ScmFileSet( getWorkingDirectory() );
288 }
289 }
290
291 public ScmRepository getScmRepository()
292 throws ScmException
293 {
294 ScmRepository repository;
295
296 try
297 {
298 repository = getScmManager().makeScmRepository( getConnectionUrl() );
299
300 ScmProviderRepository providerRepo = repository.getProviderRepository();
301
302 providerRepo.setPushChanges( pushChanges );
303
304 if ( !StringUtils.isEmpty( username ) )
305 {
306 providerRepo.setUser( username );
307 }
308
309 if ( !StringUtils.isEmpty( password ) )
310 {
311 providerRepo.setPassword( password );
312 }
313
314 if ( repository.getProviderRepository() instanceof ScmProviderRepositoryWithHost )
315 {
316 ScmProviderRepositoryWithHost repo = (ScmProviderRepositoryWithHost) repository.getProviderRepository();
317
318 loadInfosFromSettings( repo );
319
320 if ( !StringUtils.isEmpty( username ) )
321 {
322 repo.setUser( username );
323 }
324
325 if ( !StringUtils.isEmpty( password ) )
326 {
327 repo.setPassword( password );
328 }
329
330 if ( !StringUtils.isEmpty( privateKey ) )
331 {
332 repo.setPrivateKey( privateKey );
333 }
334
335 if ( !StringUtils.isEmpty( passphrase ) )
336 {
337 repo.setPassphrase( passphrase );
338 }
339 }
340
341 if ( !StringUtils.isEmpty( tagBase ) && repository.getProvider().equals( "svn" ) )
342 {
343 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
344
345 svnRepo.setTagBase( tagBase );
346 }
347 }
348 catch ( ScmRepositoryException e )
349 {
350 if ( !e.getValidationMessages().isEmpty() )
351 {
352 for ( String message : e.getValidationMessages() )
353 {
354 getLog().error( message );
355 }
356 }
357
358 throw new ScmException( "Can't load the scm provider.", e );
359 }
360 catch ( Exception e )
361 {
362 throw new ScmException( "Can't load the scm provider.", e );
363 }
364
365 return repository;
366 }
367
368 /**
369 * Load username password from settings if user has not set them in JVM properties
370 *
371 * @param repo not null
372 */
373 private void loadInfosFromSettings( ScmProviderRepositoryWithHost repo )
374 {
375 if ( username == null || password == null )
376 {
377 String host = repo.getHost();
378
379 int port = repo.getPort();
380
381 if ( port > 0 )
382 {
383 host += ":" + port;
384 }
385
386 Server server = this.settings.getServer( host );
387
388 if ( server != null )
389 {
390 if ( username == null )
391 {
392 username = server.getUsername();
393 }
394
395 if ( password == null )
396 {
397 password = decrypt( server.getPassword(), host );
398 }
399
400 if ( privateKey == null )
401 {
402 privateKey = server.getPrivateKey();
403 }
404
405 if ( passphrase == null )
406 {
407 passphrase = decrypt( server.getPassphrase(), host );
408 }
409 }
410 }
411 }
412
413 private String decrypt( String str, String server )
414 {
415 try
416 {
417 return secDispatcher.decrypt( str );
418 }
419 catch ( SecDispatcherException e )
420 {
421 getLog().warn( "Failed to decrypt password/passphrase for server " + server + ", using auth token as is" );
422 return str;
423 }
424 }
425
426 public void checkResult( ScmResult result )
427 throws MojoExecutionException
428 {
429 if ( !result.isSuccess() )
430 {
431 getLog().error( "Provider message:" );
432
433 getLog().error( result.getProviderMessage() == null ? "" : result.getProviderMessage() );
434
435 getLog().error( "Command output:" );
436
437 getLog().error( result.getCommandOutput() == null ? "" : result.getCommandOutput() );
438
439 throw new MojoExecutionException(
440 "Command failed." + StringUtils.defaultString( result.getProviderMessage() ) );
441 }
442 }
443
444 public String getIncludes()
445 {
446 return includes;
447 }
448
449 public void setIncludes( String includes )
450 {
451 this.includes = includes;
452 }
453
454 public String getExcludes()
455 {
456 return excludes;
457 }
458
459 public void setExcludes( String excludes )
460 {
461 this.excludes = excludes;
462 }
463
464 public ScmVersion getScmVersion( String versionType, String version )
465 throws MojoExecutionException
466 {
467 if ( StringUtils.isEmpty( versionType ) && StringUtils.isNotEmpty( version ) )
468 {
469 throw new MojoExecutionException( "You must specify the version type." );
470 }
471
472 if ( StringUtils.isEmpty( version ) )
473 {
474 return null;
475 }
476
477 if ( "branch".equals( versionType ) )
478 {
479 return new ScmBranch( version );
480 }
481
482 if ( "tag".equals( versionType ) )
483 {
484 return new ScmTag( version );
485 }
486
487 if ( "revision".equals( versionType ) )
488 {
489 return new ScmRevision( version );
490 }
491
492 throw new MojoExecutionException( "Unknown '" + versionType + "' version type." );
493 }
494
495 protected void handleExcludesIncludesAfterCheckoutAndExport( File checkoutDirectory )
496 throws MojoExecutionException
497 {
498 List<String> includes = new ArrayList<String>();
499
500 if ( ! StringUtils.isBlank( this.getIncludes() ) )
501 {
502 String[] tokens = StringUtils.split( this.getIncludes(), "," );
503 for ( int i = 0; i < tokens.length; ++i )
504 {
505 includes.add( tokens[i] );
506 }
507 }
508
509 List<String> excludes = new ArrayList<String>();
510
511 if ( ! StringUtils.isBlank( this.getExcludes() ) )
512 {
513 String[] tokens = StringUtils.split( this.getExcludes(), "," );
514 for ( int i = 0; i < tokens.length; ++i )
515 {
516 excludes.add( tokens[i] );
517 }
518 }
519
520 if ( includes.isEmpty() && excludes.isEmpty() )
521 {
522 return;
523 }
524
525 FileSetManager fileSetManager = new FileSetManager();
526
527 FileSet fileset = new FileSet();
528 fileset.setDirectory( checkoutDirectory.getAbsolutePath() );
529 fileset.setIncludes( excludes );//revert the order to do the delete
530 fileset.setExcludes( includes );
531 fileset.setUseDefaultExcludes( false );
532
533 try
534 {
535 fileSetManager.delete( fileset );
536 }
537 catch ( IOException e )
538 {
539 throw new MojoExecutionException( "Error found while cleaning up output directory base on excludes/includes configurations.", e );
540 }
541
542 }
543 }