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