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