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