View Javadoc
1   package org.apache.maven.scm.plugin;
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.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Map.Entry;
29  import java.util.Objects;
30  import java.util.Properties;
31  
32  import org.apache.maven.plugin.AbstractMojo;
33  import org.apache.maven.plugin.MojoExecutionException;
34  import org.apache.maven.plugins.annotations.Component;
35  import org.apache.maven.plugins.annotations.Parameter;
36  import org.apache.maven.scm.ScmBranch;
37  import org.apache.maven.scm.ScmException;
38  import org.apache.maven.scm.ScmFileSet;
39  import org.apache.maven.scm.ScmResult;
40  import org.apache.maven.scm.ScmRevision;
41  import org.apache.maven.scm.ScmTag;
42  import org.apache.maven.scm.ScmVersion;
43  import org.apache.maven.scm.manager.ScmManager;
44  import org.apache.maven.scm.provider.ScmProviderRepository;
45  import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
46  import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
47  import org.apache.maven.scm.repository.ScmRepository;
48  import org.apache.maven.scm.repository.ScmRepositoryException;
49  import org.apache.maven.settings.Server;
50  import org.apache.maven.settings.Settings;
51  import org.apache.maven.shared.model.fileset.FileSet;
52  import org.apache.maven.shared.model.fileset.util.FileSetManager;
53  import org.codehaus.plexus.util.StringUtils;
54  import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
55  import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException;
56  
57  /**
58   * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
59   * @author Olivier Lamy
60   */
61  public abstract class AbstractScmMojo
62      extends AbstractMojo
63  {
64  
65      protected static final String VERSION_TYPE_BRANCH = "branch";
66  
67      protected static final String VERSION_TYPE_REVISION = "revision";
68  
69      protected static final String VERSION_TYPE_TAG = "tag";
70  
71      protected static final String[] VALID_VERSION_TYPES = { VERSION_TYPE_BRANCH,
72              VERSION_TYPE_REVISION, VERSION_TYPE_TAG };
73  
74      /**
75       * The SCM connection URL.
76       */
77      @Parameter( property = "connectionUrl", defaultValue = "${project.scm.connection}" )
78      private String connectionUrl;
79  
80      /**
81       * The SCM connection URL for developers.
82       */
83      @Parameter( property = "developerConnectionUrl", defaultValue = "${project.scm.developerConnection}" )
84      private String developerConnectionUrl;
85  
86      /**
87       * The type of connection to use (connection or developerConnection).
88       */
89      @Parameter( property = "connectionType", defaultValue = "connection" )
90      private String connectionType;
91  
92      /**
93       * The working directory.
94       */
95      @Parameter( property = "workingDirectory" )
96      private File workingDirectory;
97  
98      /**
99       * The user name.
100      */
101     @Parameter( property = "username" )
102     private String username;
103 
104     /**
105      * The user password.
106      */
107     @Parameter( property = "password" )
108     private String password;
109 
110     /**
111      * The private key.
112      */
113     @Parameter( property = "privateKey" )
114     private String privateKey;
115 
116     /**
117      * The passphrase.
118      */
119     @Parameter( property = "passphrase" )
120     private String passphrase;
121 
122     /**
123      * The url of tags base directory (used by svn protocol). It is not
124      * necessary to set it if you use the standard svn layout
125      * (branches/tags/trunk).
126      */
127     @Parameter( property = "tagBase" )
128     private String tagBase;
129 
130     /**
131      * Comma separated list of includes file pattern.
132      */
133     @Parameter( property = "includes" )
134     private String includes;
135 
136     /**
137      * Comma separated list of excludes file pattern.
138      */
139     @Parameter( property = "excludes" )
140     private String excludes;
141 
142     @Component
143     private ScmManager manager;
144 
145     /**
146      * When this plugin requires Maven 3.0 as minimum, this component can be removed and o.a.m.s.c.SettingsDecrypter be
147      * used instead.
148      */
149     @Component( hint = "mng-4384" )
150     private SecDispatcher secDispatcher;
151 
152     /**
153      * The base directory.
154      */
155     @Parameter( property = "basedir", required = true )
156     private File basedir;
157 
158     @Parameter( defaultValue = "${settings}", readonly = true )
159     private Settings settings;
160 
161     /**
162      * List of System properties to pass to the JUnit tests.
163      */
164     @Parameter
165     private Properties systemProperties;
166 
167     /**
168      * List of provider implementations.
169      */
170     @Parameter
171     private Map<String, String> providerImplementations;
172 
173     /**
174      * Should distributed changes be pushed to the central repository?
175      * For many distributed SCMs like Git, a change like a commit
176      * is only stored in your local copy of the repository.  Pushing
177      * the change allows your to more easily share it with other users.
178      *
179      * @since 1.4
180      */
181     @Parameter( property = "pushChanges", defaultValue = "true" )
182     private boolean pushChanges;
183 
184     /**
185      * A workItem for SCMs like RTC, TFS etc, that may require additional
186      * information to perform a pushChange operation.
187      *
188      * @since 1.9.5
189      */
190     @Parameter( property = "workItem" )
191     @Deprecated
192     private String workItem;
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 File getBasedir()
269     {
270         return this.basedir;
271     }
272 
273     public void setWorkingDirectory( File workingDirectory )
274     {
275         this.workingDirectory = workingDirectory;
276     }
277 
278     public ScmManager getScmManager()
279     {
280         return manager;
281     }
282 
283     public ScmFileSet getFileSet()
284         throws IOException
285     {
286         if ( includes != null || excludes != null )
287         {
288             return new ScmFileSet( getWorkingDirectory(), includes, excludes );
289         }
290         else
291         {
292             return new ScmFileSet( getWorkingDirectory() );
293         }
294     }
295 
296     public ScmRepository getScmRepository()
297         throws ScmException
298     {
299         ScmRepository repository;
300 
301         try
302         {
303             repository = getScmManager().makeScmRepository( getConnectionUrl() );
304 
305             ScmProviderRepository providerRepo = repository.getProviderRepository();
306 
307             providerRepo.setPushChanges( pushChanges );
308 
309             if ( !StringUtils.isEmpty( workItem ) )
310             {
311                 providerRepo.setWorkItem( workItem );
312             }
313 
314             if ( !StringUtils.isEmpty( username ) )
315             {
316                 providerRepo.setUser( username );
317             }
318 
319             if ( !StringUtils.isEmpty( password ) )
320             {
321                 providerRepo.setPassword( password );
322             }
323 
324             if ( repository.getProviderRepository() instanceof ScmProviderRepositoryWithHost )
325             {
326                 ScmProviderRepositoryWithHost repo = (ScmProviderRepositoryWithHost) repository.getProviderRepository();
327 
328                 loadInfosFromSettings( repo );
329 
330                 if ( !StringUtils.isEmpty( username ) )
331                 {
332                     repo.setUser( username );
333                 }
334 
335                 if ( !StringUtils.isEmpty( password ) )
336                 {
337                     repo.setPassword( password );
338                 }
339 
340                 if ( !StringUtils.isEmpty( privateKey ) )
341                 {
342                     repo.setPrivateKey( privateKey );
343                 }
344 
345                 if ( !StringUtils.isEmpty( passphrase ) )
346                 {
347                     repo.setPassphrase( passphrase );
348                 }
349             }
350 
351             if ( !StringUtils.isEmpty( tagBase ) && repository.getProvider().equals( "svn" ) )
352             {
353                 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
354 
355                 svnRepo.setTagBase( tagBase );
356             }
357         }
358         catch ( ScmRepositoryException e )
359         {
360             if ( !e.getValidationMessages().isEmpty() )
361             {
362                 for ( String message : e.getValidationMessages() )
363                 {
364                     getLog().error( message );
365                 }
366             }
367 
368             throw new ScmException( "Can't load the scm provider.", e );
369         }
370         catch ( Exception e )
371         {
372             throw new ScmException( "Can't load the scm provider.", e );
373         }
374 
375         return repository;
376     }
377 
378     /**
379      * Load username password from settings if user has not set them in JVM properties
380      *
381      * @param repo not null
382      */
383     private void loadInfosFromSettings( ScmProviderRepositoryWithHost repo )
384     {
385         if ( username == null || password == null )
386         {
387             String host = repo.getHost();
388 
389             int port = repo.getPort();
390 
391             if ( port > 0 )
392             {
393                 host += ":" + port;
394             }
395 
396             Server server = this.settings.getServer( host );
397 
398             if ( server != null )
399             {
400                 if ( username == null )
401                 {
402                     username = server.getUsername();
403                 }
404 
405                 if ( password == null )
406                 {
407                     password = decrypt( server.getPassword(), host );
408                 }
409 
410                 if ( privateKey == null )
411                 {
412                     privateKey = server.getPrivateKey();
413                 }
414 
415                 if ( passphrase == null )
416                 {
417                     passphrase = decrypt( server.getPassphrase(), host );
418                 }
419             }
420         }
421     }
422 
423     private String decrypt( String str, String server )
424     {
425         try
426         {
427             return secDispatcher.decrypt( str );
428         }
429         catch ( SecDispatcherException e )
430         {
431             getLog().warn( "Failed to decrypt password/passphrase for server " + server + ", using auth token as is" );
432             return str;
433         }
434     }
435 
436     public void checkResult( ScmResult result )
437         throws MojoExecutionException
438     {
439         if ( !result.isSuccess() )
440         {
441             getLog().error( "Provider message:" );
442 
443             getLog().error( result.getProviderMessage() == null ? "" : result.getProviderMessage() );
444 
445             getLog().error( "Command output:" );
446 
447             getLog().error( result.getCommandOutput() == null ? "" : result.getCommandOutput() );
448 
449             throw new MojoExecutionException(
450                 "Command failed: " + Objects.toString( result.getProviderMessage() ) );
451         }
452     }
453 
454     public String getIncludes()
455     {
456         return includes;
457     }
458 
459     public void setIncludes( String includes )
460     {
461         this.includes = includes;
462     }
463 
464     public String getExcludes()
465     {
466         return excludes;
467     }
468 
469     public void setExcludes( String excludes )
470     {
471         this.excludes = excludes;
472     }
473 
474     public ScmVersion getScmVersion( String versionType, String version )
475         throws MojoExecutionException
476     {
477         if ( StringUtils.isEmpty( versionType ) && StringUtils.isNotEmpty( version ) )
478         {
479             throw new MojoExecutionException( "You must specify the version type." );
480         }
481 
482         if ( StringUtils.isEmpty( version ) )
483         {
484             return null;
485         }
486 
487         if ( VERSION_TYPE_BRANCH.equals( versionType ) )
488         {
489             return new ScmBranch( version );
490         }
491 
492         if ( VERSION_TYPE_TAG.equals( versionType ) )
493         {
494             return new ScmTag( version );
495         }
496 
497         if ( VERSION_TYPE_REVISION.equals( versionType ) )
498         {
499             return new ScmRevision( version );
500         }
501 
502         throw new MojoExecutionException( "Unknown '" + versionType + "' version type." );
503     }
504 
505     protected void handleExcludesIncludesAfterCheckoutAndExport( File checkoutDirectory )
506         throws MojoExecutionException
507     {
508         List<String> includes = new ArrayList<String>();
509 
510         if ( ! StringUtils.isBlank( this.getIncludes() ) )
511         {
512             String[] tokens = StringUtils.split( this.getIncludes(), "," );
513             for ( int i = 0; i < tokens.length; ++i )
514             {
515                 includes.add( tokens[i] );
516             }
517         }
518 
519         List<String> excludes = new ArrayList<String>();
520 
521         if ( ! StringUtils.isBlank( this.getExcludes() ) )
522         {
523             String[] tokens = StringUtils.split( this.getExcludes(), "," );
524             for ( int i = 0; i < tokens.length; ++i )
525             {
526                 excludes.add( tokens[i] );
527             }
528         }
529 
530         if ( includes.isEmpty() && excludes.isEmpty() )
531         {
532             return;
533         }
534 
535         FileSetManager fileSetManager = new FileSetManager();
536 
537         FileSet fileset = new FileSet();
538         fileset.setDirectory( checkoutDirectory.getAbsolutePath() );
539         fileset.setIncludes( excludes ); // revert the order to do the delete
540         fileset.setExcludes( includes );
541         fileset.setUseDefaultExcludes( false );
542 
543         try
544         {
545             fileSetManager.delete( fileset );
546         }
547         catch ( IOException e )
548         {
549             throw new MojoExecutionException( "Error found while cleaning up output directory base on "
550                 + "excludes/includes configurations.", e );
551         }
552 
553     }
554 }