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.settings.building.SettingsProblem;
52  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
53  import org.apache.maven.settings.crypto.SettingsDecrypter;
54  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
55  import org.apache.maven.shared.model.fileset.FileSet;
56  import org.apache.maven.shared.model.fileset.util.FileSetManager;
57  import org.codehaus.plexus.util.StringUtils;
58  
59  /**
60   * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
61   * @author Olivier Lamy
62   */
63  public abstract class AbstractScmMojo
64      extends AbstractMojo
65  {
66  
67      protected static final String VERSION_TYPE_BRANCH = "branch";
68  
69      protected static final String VERSION_TYPE_REVISION = "revision";
70  
71      protected static final String VERSION_TYPE_TAG = "tag";
72  
73      protected static final String[] VALID_VERSION_TYPES = { VERSION_TYPE_BRANCH,
74              VERSION_TYPE_REVISION, VERSION_TYPE_TAG };
75  
76      /**
77       * The SCM connection URL.
78       */
79      @Parameter( property = "connectionUrl", defaultValue = "${project.scm.connection}" )
80      private String connectionUrl;
81  
82      /**
83       * The SCM connection URL for developers.
84       */
85      @Parameter( property = "developerConnectionUrl", defaultValue = "${project.scm.developerConnection}" )
86      private String developerConnectionUrl;
87  
88      /**
89       * The type of connection to use (connection or developerConnection).
90       */
91      @Parameter( property = "connectionType", defaultValue = "connection" )
92      private String connectionType;
93  
94      /**
95       * The working directory.
96       */
97      @Parameter( property = "workingDirectory" )
98      private File workingDirectory;
99  
100     /**
101      * The user name.
102      */
103     @Parameter( property = "username" )
104     private String username;
105 
106     /**
107      * The user password.
108      */
109     @Parameter( property = "password" )
110     private String password;
111 
112     /**
113      * The private key.
114      */
115     @Parameter( property = "privateKey" )
116     private String privateKey;
117 
118     /**
119      * The passphrase.
120      */
121     @Parameter( property = "passphrase" )
122     private String passphrase;
123 
124     /**
125      * The url of tags base directory (used by svn protocol). It is not
126      * necessary to set it if you use the standard svn layout
127      * (branches/tags/trunk).
128      */
129     @Parameter( property = "tagBase" )
130     private String tagBase;
131 
132     /**
133      * Comma separated list of includes file pattern.
134      */
135     @Parameter( property = "includes" )
136     private String includes;
137 
138     /**
139      * Comma separated list of excludes file pattern.
140      */
141     @Parameter( property = "excludes" )
142     private String excludes;
143 
144     @Component
145     private ScmManager manager;
146 
147     @Component
148     private SettingsDecrypter settingsDecrypter;
149 
150     /**
151      * The base directory.
152      */
153     @Parameter( property = "basedir", required = true )
154     private File basedir;
155 
156     @Parameter( defaultValue = "${settings}", readonly = true )
157     private Settings settings;
158 
159     /**
160      * List of System properties to pass to the JUnit tests.
161      */
162     @Parameter
163     private Properties systemProperties;
164 
165     /**
166      * List of provider implementations.
167      */
168     @Parameter
169     private Map<String, String> providerImplementations;
170 
171     /**
172      * Should distributed changes be pushed to the central repository?
173      * For many distributed SCMs like Git, a change like a commit
174      * is only stored in your local copy of the repository.  Pushing
175      * the change allows your to more easily share it with other users.
176      *
177      * @since 1.4
178      */
179     @Parameter( property = "pushChanges", defaultValue = "true" )
180     private boolean pushChanges;
181 
182     /**
183      * A workItem for SCMs like RTC, TFS etc, that may require additional
184      * information to perform a pushChange operation.
185      *
186      * @since 1.9.5
187      */
188     @Parameter( property = "workItem" )
189     @Deprecated
190     private String workItem;
191 
192     /** {@inheritDoc} */
193     public void execute()
194         throws MojoExecutionException
195     {
196         if ( systemProperties != null )
197         {
198             // Add all system properties configured by the user
199             Iterator<Object> iter = systemProperties.keySet().iterator();
200 
201             while ( iter.hasNext() )
202             {
203                 String key = (String) iter.next();
204 
205                 String value = systemProperties.getProperty( key );
206 
207                 System.setProperty( key, value );
208             }
209         }
210 
211         if ( providerImplementations != null && !providerImplementations.isEmpty() )
212         {
213             for ( Entry<String, String> entry : providerImplementations.entrySet() )
214             {
215                 String providerType = entry.getKey();
216                 String providerImplementation = entry.getValue();
217                 getLog().info(
218                                "Change the default '" + providerType + "' provider implementation to '"
219                                    + providerImplementation + "'." );
220                 getScmManager().setScmProviderImplementation( providerType, providerImplementation );
221             }
222         }
223     }
224 
225     protected void setConnectionType( String connectionType )
226     {
227         this.connectionType = connectionType;
228     }
229 
230     public String getConnectionUrl()
231     {
232         boolean requireDeveloperConnection = !"connection".equals( connectionType.toLowerCase() );
233         if ( StringUtils.isNotEmpty( connectionUrl ) && !requireDeveloperConnection )
234         {
235             return connectionUrl;
236         }
237         else if ( StringUtils.isNotEmpty( developerConnectionUrl ) )
238         {
239             return developerConnectionUrl;
240         }
241         if ( requireDeveloperConnection )
242         {
243             throw new NullPointerException( "You need to define a developerConnectionUrl parameter" );
244         }
245         else
246         {
247             throw new NullPointerException( "You need to define a connectionUrl parameter" );
248         }
249     }
250 
251     public void setConnectionUrl( String connectionUrl )
252     {
253         this.connectionUrl = connectionUrl;
254     }
255 
256     public File getWorkingDirectory()
257     {
258         if ( workingDirectory == null )
259         {
260             return basedir;
261         }
262 
263         return workingDirectory;
264     }
265 
266     public File getBasedir()
267     {
268         return this.basedir;
269     }
270 
271     public void setWorkingDirectory( File workingDirectory )
272     {
273         this.workingDirectory = workingDirectory;
274     }
275 
276     public ScmManager getScmManager()
277     {
278         return manager;
279     }
280 
281     public ScmFileSet getFileSet()
282         throws IOException
283     {
284         if ( includes != null || excludes != null )
285         {
286             return new ScmFileSet( getWorkingDirectory(), includes, excludes );
287         }
288         else
289         {
290             return new ScmFileSet( getWorkingDirectory() );
291         }
292     }
293 
294     public ScmRepository getScmRepository()
295         throws ScmException
296     {
297         ScmRepository repository;
298 
299         try
300         {
301             repository = getScmManager().makeScmRepository( getConnectionUrl() );
302 
303             ScmProviderRepository providerRepo = repository.getProviderRepository();
304 
305             providerRepo.setPushChanges( pushChanges );
306 
307             if ( !StringUtils.isEmpty( workItem ) )
308             {
309                 providerRepo.setWorkItem( workItem );
310             }
311 
312             if ( !StringUtils.isEmpty( username ) )
313             {
314                 providerRepo.setUser( username );
315             }
316 
317             if ( !StringUtils.isEmpty( password ) )
318             {
319                 providerRepo.setPassword( password );
320             }
321 
322             if ( repository.getProviderRepository() instanceof ScmProviderRepositoryWithHost )
323             {
324                 ScmProviderRepositoryWithHost repo = (ScmProviderRepositoryWithHost) repository.getProviderRepository();
325 
326                 loadInfosFromSettings( repo );
327 
328                 if ( !StringUtils.isEmpty( username ) )
329                 {
330                     repo.setUser( username );
331                 }
332 
333                 if ( !StringUtils.isEmpty( password ) )
334                 {
335                     repo.setPassword( password );
336                 }
337 
338                 if ( !StringUtils.isEmpty( privateKey ) )
339                 {
340                     repo.setPrivateKey( privateKey );
341                 }
342 
343                 if ( !StringUtils.isEmpty( passphrase ) )
344                 {
345                     repo.setPassphrase( passphrase );
346                 }
347             }
348 
349             if ( !StringUtils.isEmpty( tagBase ) && repository.getProvider().equals( "svn" ) )
350             {
351                 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
352 
353                 svnRepo.setTagBase( tagBase );
354             }
355         }
356         catch ( ScmRepositoryException e )
357         {
358             if ( !e.getValidationMessages().isEmpty() )
359             {
360                 for ( String message : e.getValidationMessages() )
361                 {
362                     getLog().error( message );
363                 }
364             }
365 
366             throw new ScmException( "Can't load the scm provider.", e );
367         }
368         catch ( Exception e )
369         {
370             throw new ScmException( "Can't load the scm provider.", e );
371         }
372 
373         return repository;
374     }
375 
376     /**
377      * Load username password from settings if user has not set them in JVM properties
378      *
379      * @param repo not null
380      */
381     private void loadInfosFromSettings( ScmProviderRepositoryWithHost repo )
382     {
383         if ( username == null || password == null )
384         {
385             String host = repo.getHost();
386 
387             int port = repo.getPort();
388 
389             if ( port > 0 )
390             {
391                 host += ":" + port;
392             }
393 
394             Server server = this.settings.getServer( host );
395 
396             if ( server != null )
397             {
398                 server = decrypt( server );
399 
400                 if ( username == null )
401                 {
402                     username = server.getUsername();
403                 }
404 
405                 if ( password == null )
406                 {
407                     password = server.getPassword();
408                 }
409 
410                 if ( privateKey == null )
411                 {
412                     privateKey = server.getPrivateKey();
413                 }
414 
415                 if ( passphrase == null )
416                 {
417                     passphrase = server.getPassphrase();
418                 }
419             }
420         }
421     }
422 
423     private Server decrypt( Server server )
424     {
425         SettingsDecryptionResult result = settingsDecrypter.decrypt( new DefaultSettingsDecryptionRequest( server ) );
426         for ( SettingsProblem problem : result.getProblems() )
427         {
428             getLog().error( problem.getMessage(), problem.getException() );
429         }
430 
431         return result.getServer();
432     }
433 
434     public void checkResult( ScmResult result )
435         throws MojoExecutionException
436     {
437         if ( !result.isSuccess() )
438         {
439             getLog().error( "Provider message:" );
440 
441             getLog().error( result.getProviderMessage() == null ? "" : result.getProviderMessage() );
442 
443             getLog().error( "Command output:" );
444 
445             getLog().error( result.getCommandOutput() == null ? "" : result.getCommandOutput() );
446 
447             throw new MojoExecutionException(
448                 "Command failed: " + Objects.toString( result.getProviderMessage() ) );
449         }
450     }
451 
452     public String getIncludes()
453     {
454         return includes;
455     }
456 
457     public void setIncludes( String includes )
458     {
459         this.includes = includes;
460     }
461 
462     public String getExcludes()
463     {
464         return excludes;
465     }
466 
467     public void setExcludes( String excludes )
468     {
469         this.excludes = excludes;
470     }
471 
472     public ScmVersion getScmVersion( String versionType, String version )
473         throws MojoExecutionException
474     {
475         if ( StringUtils.isEmpty( versionType ) && StringUtils.isNotEmpty( version ) )
476         {
477             throw new MojoExecutionException( "You must specify the version type." );
478         }
479 
480         if ( StringUtils.isEmpty( version ) )
481         {
482             return null;
483         }
484 
485         if ( VERSION_TYPE_BRANCH.equals( versionType ) )
486         {
487             return new ScmBranch( version );
488         }
489 
490         if ( VERSION_TYPE_TAG.equals( versionType ) )
491         {
492             return new ScmTag( version );
493         }
494 
495         if ( VERSION_TYPE_REVISION.equals( versionType ) )
496         {
497             return new ScmRevision( version );
498         }
499 
500         throw new MojoExecutionException( "Unknown '" + versionType + "' version type." );
501     }
502 
503     protected void handleExcludesIncludesAfterCheckoutAndExport( File checkoutDirectory )
504         throws MojoExecutionException
505     {
506         List<String> includes = new ArrayList<String>();
507 
508         if ( ! StringUtils.isBlank( this.getIncludes() ) )
509         {
510             String[] tokens = StringUtils.split( this.getIncludes(), "," );
511             for ( int i = 0; i < tokens.length; ++i )
512             {
513                 includes.add( tokens[i] );
514             }
515         }
516 
517         List<String> excludes = new ArrayList<String>();
518 
519         if ( ! StringUtils.isBlank( this.getExcludes() ) )
520         {
521             String[] tokens = StringUtils.split( this.getExcludes(), "," );
522             for ( int i = 0; i < tokens.length; ++i )
523             {
524                 excludes.add( tokens[i] );
525             }
526         }
527 
528         if ( includes.isEmpty() && excludes.isEmpty() )
529         {
530             return;
531         }
532 
533         FileSetManager fileSetManager = new FileSetManager();
534 
535         FileSet fileset = new FileSet();
536         fileset.setDirectory( checkoutDirectory.getAbsolutePath() );
537         fileset.setIncludes( excludes ); // revert the order to do the delete
538         fileset.setExcludes( includes );
539         fileset.setUseDefaultExcludes( false );
540 
541         try
542         {
543             fileSetManager.delete( fileset );
544         }
545         catch ( IOException e )
546         {
547             throw new MojoExecutionException( "Error found while cleaning up output directory base on "
548                 + "excludes/includes configurations.", e );
549         }
550 
551     }
552 }