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 (used by svn, starteam and perforce protocol).
100      */
101     @Parameter( property = "username" )
102     private String username;
103 
104     /**
105      * The user password (used by svn, starteam and perforce protocol).
106      */
107     @Parameter( property = "password" )
108     private String password;
109 
110     /**
111      * The private key (used by java svn).
112      */
113     @Parameter( property = "privateKey" )
114     private String privateKey;
115 
116     /**
117      * The passphrase (used by java svn).
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     private String workItem;
192 
193     /** {@inheritDoc} */
194     public void execute()
195         throws MojoExecutionException
196     {
197         if ( systemProperties != null )
198         {
199             // Add all system properties configured by the user
200             Iterator<Object> iter = systemProperties.keySet().iterator();
201 
202             while ( iter.hasNext() )
203             {
204                 String key = (String) iter.next();
205 
206                 String value = systemProperties.getProperty( key );
207 
208                 System.setProperty( key, value );
209             }
210         }
211 
212         if ( providerImplementations != null && !providerImplementations.isEmpty() )
213         {
214             for ( Entry<String, String> entry : providerImplementations.entrySet() )
215             {
216                 String providerType = entry.getKey();
217                 String providerImplementation = entry.getValue();
218                 getLog().info(
219                                "Change the default '" + providerType + "' provider implementation to '"
220                                    + providerImplementation + "'." );
221                 getScmManager().setScmProviderImplementation( providerType, providerImplementation );
222             }
223         }
224     }
225 
226     protected void setConnectionType( String connectionType )
227     {
228         this.connectionType = connectionType;
229     }
230 
231     public String getConnectionUrl()
232     {
233         boolean requireDeveloperConnection = !"connection".equals( connectionType.toLowerCase() );
234         if ( StringUtils.isNotEmpty( connectionUrl ) && !requireDeveloperConnection )
235         {
236             return connectionUrl;
237         }
238         else if ( StringUtils.isNotEmpty( developerConnectionUrl ) )
239         {
240             return developerConnectionUrl;
241         }
242         if ( requireDeveloperConnection )
243         {
244             throw new NullPointerException( "You need to define a developerConnectionUrl parameter" );
245         }
246         else
247         {
248             throw new NullPointerException( "You need to define a connectionUrl parameter" );
249         }
250     }
251 
252     public void setConnectionUrl( String connectionUrl )
253     {
254         this.connectionUrl = connectionUrl;
255     }
256 
257     public File getWorkingDirectory()
258     {
259         if ( workingDirectory == null )
260         {
261             return basedir;
262         }
263 
264         return workingDirectory;
265     }
266 
267     public File getBasedir()
268     {
269         return this.basedir;
270     }
271 
272     public void setWorkingDirectory( File workingDirectory )
273     {
274         this.workingDirectory = workingDirectory;
275     }
276 
277     public ScmManager getScmManager()
278     {
279         return manager;
280     }
281 
282     public ScmFileSet getFileSet()
283         throws IOException
284     {
285         if ( includes != null || excludes != null )
286         {
287             return new ScmFileSet( getWorkingDirectory(), includes, excludes );
288         }
289         else
290         {
291             return new ScmFileSet( getWorkingDirectory() );
292         }
293     }
294 
295     public ScmRepository getScmRepository()
296         throws ScmException
297     {
298         ScmRepository repository;
299 
300         try
301         {
302             repository = getScmManager().makeScmRepository( getConnectionUrl() );
303 
304             ScmProviderRepository providerRepo = repository.getProviderRepository();
305 
306             providerRepo.setPushChanges( pushChanges );
307 
308             if ( !StringUtils.isEmpty( workItem ) )
309             {
310                 providerRepo.setWorkItem( workItem );
311             }
312             
313             if ( !StringUtils.isEmpty( username ) )
314             {
315                 providerRepo.setUser( username );
316             }
317 
318             if ( !StringUtils.isEmpty( password ) )
319             {
320                 providerRepo.setPassword( password );
321             }
322 
323             if ( repository.getProviderRepository() instanceof ScmProviderRepositoryWithHost )
324             {
325                 ScmProviderRepositoryWithHost repo = (ScmProviderRepositoryWithHost) repository.getProviderRepository();
326 
327                 loadInfosFromSettings( repo );
328 
329                 if ( !StringUtils.isEmpty( username ) )
330                 {
331                     repo.setUser( username );
332                 }
333 
334                 if ( !StringUtils.isEmpty( password ) )
335                 {
336                     repo.setPassword( password );
337                 }
338 
339                 if ( !StringUtils.isEmpty( privateKey ) )
340                 {
341                     repo.setPrivateKey( privateKey );
342                 }
343 
344                 if ( !StringUtils.isEmpty( passphrase ) )
345                 {
346                     repo.setPassphrase( passphrase );
347                 }
348             }
349 
350             if ( !StringUtils.isEmpty( tagBase ) && repository.getProvider().equals( "svn" ) )
351             {
352                 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
353 
354                 svnRepo.setTagBase( tagBase );
355             }
356         }
357         catch ( ScmRepositoryException e )
358         {
359             if ( !e.getValidationMessages().isEmpty() )
360             {
361                 for ( String message : e.getValidationMessages() )
362                 {
363                     getLog().error( message );
364                 }
365             }
366 
367             throw new ScmException( "Can't load the scm provider.", e );
368         }
369         catch ( Exception e )
370         {
371             throw new ScmException( "Can't load the scm provider.", e );
372         }
373 
374         return repository;
375     }
376 
377     /**
378      * Load username password from settings if user has not set them in JVM properties
379      *
380      * @param repo not null
381      */
382     private void loadInfosFromSettings( ScmProviderRepositoryWithHost repo )
383     {
384         if ( username == null || password == null )
385         {
386             String host = repo.getHost();
387 
388             int port = repo.getPort();
389 
390             if ( port > 0 )
391             {
392                 host += ":" + port;
393             }
394 
395             Server server = this.settings.getServer( host );
396 
397             if ( server != null )
398             {
399                 if ( username == null )
400                 {
401                     username = server.getUsername();
402                 }
403 
404                 if ( password == null )
405                 {
406                     password = decrypt( server.getPassword(), host );
407                 }
408 
409                 if ( privateKey == null )
410                 {
411                     privateKey = server.getPrivateKey();
412                 }
413 
414                 if ( passphrase == null )
415                 {
416                     passphrase = decrypt( server.getPassphrase(), host );
417                 }
418             }
419         }
420     }
421 
422     private String decrypt( String str, String server )
423     {
424         try
425         {
426             return secDispatcher.decrypt( str );
427         }
428         catch ( SecDispatcherException e )
429         {
430             getLog().warn( "Failed to decrypt password/passphrase for server " + server + ", using auth token as is" );
431             return str;
432         }
433     }
434 
435     public void checkResult( ScmResult result )
436         throws MojoExecutionException
437     {
438         if ( !result.isSuccess() )
439         {
440             getLog().error( "Provider message:" );
441 
442             getLog().error( result.getProviderMessage() == null ? "" : result.getProviderMessage() );
443 
444             getLog().error( "Command output:" );
445 
446             getLog().error( result.getCommandOutput() == null ? "" : result.getCommandOutput() );
447 
448             throw new MojoExecutionException(
449                 "Command failed: " + Objects.toString( result.getProviderMessage() ) );
450         }
451     }
452 
453     public String getIncludes()
454     {
455         return includes;
456     }
457 
458     public void setIncludes( String includes )
459     {
460         this.includes = includes;
461     }
462 
463     public String getExcludes()
464     {
465         return excludes;
466     }
467 
468     public void setExcludes( String excludes )
469     {
470         this.excludes = excludes;
471     }
472 
473     public ScmVersion getScmVersion( String versionType, String version )
474         throws MojoExecutionException
475     {
476         if ( StringUtils.isEmpty( versionType ) && StringUtils.isNotEmpty( version ) )
477         {
478             throw new MojoExecutionException( "You must specify the version type." );
479         }
480 
481         if ( StringUtils.isEmpty( version ) )
482         {
483             return null;
484         }
485 
486         if ( VERSION_TYPE_BRANCH.equals( versionType ) )
487         {
488             return new ScmBranch( version );
489         }
490 
491         if ( VERSION_TYPE_TAG.equals( versionType ) )
492         {
493             return new ScmTag( version );
494         }
495 
496         if ( VERSION_TYPE_REVISION.equals( versionType ) )
497         {
498             return new ScmRevision( version );
499         }
500 
501         throw new MojoExecutionException( "Unknown '" + versionType + "' version type." );
502     }
503 
504     protected void handleExcludesIncludesAfterCheckoutAndExport( File checkoutDirectory )
505         throws MojoExecutionException
506     {
507         List<String> includes = new ArrayList<String>();
508 
509         if ( ! StringUtils.isBlank( this.getIncludes() ) )
510         {
511             String[] tokens = StringUtils.split( this.getIncludes(), "," );
512             for ( int i = 0; i < tokens.length; ++i )
513             {
514                 includes.add( tokens[i] );
515             }
516         }
517 
518         List<String> excludes = new ArrayList<String>();
519 
520         if ( ! StringUtils.isBlank( this.getExcludes() ) )
521         {
522             String[] tokens = StringUtils.split( this.getExcludes(), "," );
523             for ( int i = 0; i < tokens.length; ++i )
524             {
525                 excludes.add( tokens[i] );
526             }
527         }
528 
529         if ( includes.isEmpty() && excludes.isEmpty() )
530         {
531             return;
532         }
533 
534         FileSetManager fileSetManager = new FileSetManager();
535 
536         FileSet fileset = new FileSet();
537         fileset.setDirectory( checkoutDirectory.getAbsolutePath() );
538         fileset.setIncludes( excludes ); // revert the order to do the delete
539         fileset.setExcludes( includes );
540         fileset.setUseDefaultExcludes( false );
541 
542         try
543         {
544             fileSetManager.delete( fileset );
545         }
546         catch ( IOException e )
547         {
548             throw new MojoExecutionException( "Error found while cleaning up output directory base on "
549                 + "excludes/includes configurations.", e );
550         }
551 
552     }
553 }