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 = "connectionUrl", 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 void setWorkingDirectory( File workingDirectory )
248     {
249         this.workingDirectory = workingDirectory;
250     }
251 
252     public ScmManager getScmManager()
253     {
254         return manager;
255     }
256 
257     public ScmFileSet getFileSet()
258         throws IOException
259     {
260         if ( includes != null || excludes != null )
261         {
262             return new ScmFileSet( getWorkingDirectory(), includes, excludes );
263         }
264         else
265         {
266             return new ScmFileSet( getWorkingDirectory() );
267         }
268     }
269 
270     public ScmRepository getScmRepository()
271         throws ScmException
272     {
273         ScmRepository repository;
274 
275         try
276         {
277             repository = getScmManager().makeScmRepository( getConnectionUrl() );
278 
279             ScmProviderRepository providerRepo = repository.getProviderRepository();
280             
281             providerRepo.setPushChanges( pushChanges );
282 
283             if ( !StringUtils.isEmpty( username ) )
284             {
285                 providerRepo.setUser( username );
286             }
287 
288             if ( !StringUtils.isEmpty( password ) )
289             {
290                 providerRepo.setPassword( password );
291             }
292 
293             if ( repository.getProviderRepository() instanceof ScmProviderRepositoryWithHost )
294             {
295                 ScmProviderRepositoryWithHost repo = (ScmProviderRepositoryWithHost) repository.getProviderRepository();
296 
297                 loadInfosFromSettings( repo );
298 
299                 if ( !StringUtils.isEmpty( username ) )
300                 {
301                     repo.setUser( username );
302                 }
303 
304                 if ( !StringUtils.isEmpty( password ) )
305                 {
306                     repo.setPassword( password );
307                 }
308 
309                 if ( !StringUtils.isEmpty( privateKey ) )
310                 {
311                     repo.setPrivateKey( privateKey );
312                 }
313 
314                 if ( !StringUtils.isEmpty( passphrase ) )
315                 {
316                     repo.setPassphrase( passphrase );
317                 }
318             }
319 
320             if ( !StringUtils.isEmpty( tagBase ) && repository.getProvider().equals( "svn" ) )
321             {
322                 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
323 
324                 svnRepo.setTagBase( tagBase );
325             }
326         }
327         catch ( ScmRepositoryException e )
328         {
329             if ( !e.getValidationMessages().isEmpty() )
330             {
331                 for ( String message : e.getValidationMessages() )
332                 {
333                     getLog().error( message );
334                 }
335             }
336 
337             throw new ScmException( "Can't load the scm provider.", e );
338         }
339         catch ( Exception e )
340         {
341             throw new ScmException( "Can't load the scm provider.", e );
342         }
343 
344         return repository;
345     }
346 
347     /**
348      * Load username password from settings if user has not set them in JVM properties
349      *
350      * @param repo not null
351      */
352     private void loadInfosFromSettings( ScmProviderRepositoryWithHost repo )
353     {
354         if ( username == null || password == null )
355         {
356             String host = repo.getHost();
357 
358             int port = repo.getPort();
359 
360             if ( port > 0 )
361             {
362                 host += ":" + port;
363             }
364 
365             Server server = this.settings.getServer( host );
366 
367             if ( server != null )
368             {
369                 if ( username == null )
370                 {
371                     username = server.getUsername();
372                 }
373 
374                 if ( password == null )
375                 {
376                     password = decrypt( server.getPassword(), host );
377                 }
378 
379                 if ( privateKey == null )
380                 {
381                     privateKey = server.getPrivateKey();
382                 }
383 
384                 if ( passphrase == null )
385                 {
386                     passphrase = decrypt( server.getPassphrase(), host );
387                 }
388             }
389         }
390     }
391 
392     private String decrypt( String str, String server )
393     {
394         try
395         {
396             return secDispatcher.decrypt( str );
397         }
398         catch ( SecDispatcherException e )
399         {
400             getLog().warn( "Failed to decrypt password/passphrase for server " + server + ", using auth token as is" );
401             return str;
402         }
403     }
404 
405     public void checkResult( ScmResult result )
406         throws MojoExecutionException
407     {
408         if ( !result.isSuccess() )
409         {
410             getLog().error( "Provider message:" );
411 
412             getLog().error( result.getProviderMessage() == null ? "" : result.getProviderMessage() );
413 
414             getLog().error( "Command output:" );
415 
416             getLog().error( result.getCommandOutput() == null ? "" : result.getCommandOutput() );
417 
418             throw new MojoExecutionException(
419                 "Command failed." + StringUtils.defaultString( result.getProviderMessage() ) );
420         }
421     }
422 
423     public String getIncludes()
424     {
425         return includes;
426     }
427 
428     public void setIncludes( String includes )
429     {
430         this.includes = includes;
431     }
432 
433     public String getExcludes()
434     {
435         return excludes;
436     }
437 
438     public void setExcludes( String excludes )
439     {
440         this.excludes = excludes;
441     }
442 
443     public ScmVersion getScmVersion( String versionType, String version )
444         throws MojoExecutionException
445     {
446         if ( StringUtils.isEmpty( versionType ) && StringUtils.isNotEmpty( version ) )
447         {
448             throw new MojoExecutionException( "You must specify the version type." );
449         }
450 
451         if ( StringUtils.isEmpty( version ) )
452         {
453             return null;
454         }
455 
456         if ( "branch".equals( versionType ) )
457         {
458             return new ScmBranch( version );
459         }
460 
461         if ( "tag".equals( versionType ) )
462         {
463             return new ScmTag( version );
464         }
465 
466         if ( "revision".equals( versionType ) )
467         {
468             return new ScmRevision( version );
469         }
470 
471         throw new MojoExecutionException( "Unknown '" + versionType + "' version type." );
472     }
473     
474     protected void handleExcludesIncludesAfterCheckoutAndExport( File checkoutDirectory )
475         throws MojoExecutionException
476     {
477         List<String> includes = new ArrayList<String>();
478 
479         if ( ! StringUtils.isBlank( this.getIncludes() ) )
480         {
481             String[] tokens = StringUtils.split( this.getIncludes(), "," );
482             for ( int i = 0; i < tokens.length; ++i )
483             {
484                 includes.add( tokens[i] );
485             }
486         }
487 
488         List<String> excludes = new ArrayList<String>();
489 
490         if ( ! StringUtils.isBlank( this.getExcludes() ) )
491         {
492             String[] tokens = StringUtils.split( this.getExcludes(), "," );
493             for ( int i = 0; i < tokens.length; ++i )
494             {
495                 excludes.add( tokens[i] );
496             }
497         }
498         
499         if ( includes.isEmpty() &&  excludes.isEmpty() )
500         {
501             return;
502         }
503 
504         FileSetManager fileSetManager = new FileSetManager();
505 
506         FileSet fileset = new FileSet();
507         fileset.setDirectory( checkoutDirectory.getAbsolutePath() );
508         fileset.setIncludes( excludes );//revert the order to do the delete
509         fileset.setExcludes( includes );
510         fileset.setUseDefaultExcludes( false );
511 
512         try
513         {
514             fileSetManager.delete( fileset );
515         }
516         catch ( IOException e )
517         {
518             throw new MojoExecutionException( "Error found while cleaning up output directory base on excludes/includes configurations.", e );
519         }
520 
521     }
522 }