001    package org.apache.maven.scm.plugin;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     * http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.File;
023    import java.io.IOException;
024    import java.util.ArrayList;
025    import java.util.Iterator;
026    import java.util.List;
027    import java.util.Map;
028    import java.util.Map.Entry;
029    import java.util.Properties;
030    
031    import org.apache.maven.plugin.AbstractMojo;
032    import org.apache.maven.plugin.MojoExecutionException;
033    import org.apache.maven.scm.ScmBranch;
034    import org.apache.maven.scm.ScmException;
035    import org.apache.maven.scm.ScmFileSet;
036    import org.apache.maven.scm.ScmResult;
037    import org.apache.maven.scm.ScmRevision;
038    import org.apache.maven.scm.ScmTag;
039    import org.apache.maven.scm.ScmVersion;
040    import org.apache.maven.scm.manager.ScmManager;
041    import org.apache.maven.scm.provider.ScmProviderRepository;
042    import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
043    import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
044    import org.apache.maven.scm.repository.ScmRepository;
045    import org.apache.maven.scm.repository.ScmRepositoryException;
046    import org.apache.maven.settings.Server;
047    import org.apache.maven.settings.Settings;
048    import org.apache.maven.shared.model.fileset.FileSet;
049    import org.apache.maven.shared.model.fileset.util.FileSetManager;
050    import org.codehaus.plexus.util.StringUtils;
051    import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
052    import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException;
053    
054    /**
055     * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
056     * @author Olivier Lamy
057     * @version $Id: AbstractScmMojo.java 1069361 2011-02-10 12:38:18Z bentmann $
058     */
059    public abstract class AbstractScmMojo
060        extends AbstractMojo
061    {
062        /**
063         * The SCM connection URL.
064         *
065         * @parameter expression="${connectionUrl}" default-value="${project.scm.connection}"
066         */
067        private String connectionUrl;
068    
069        /**
070         * The SCM connection URL for developers.
071         *
072         * @parameter expression="${connectionUrl}" default-value="${project.scm.developerConnection}"
073         */
074        private String developerConnectionUrl;
075    
076        /**
077         * The type of connection to use (connection or developerConnection).
078         *
079         * @parameter expression="${connectionType}" default-value="connection"
080         */
081        private String connectionType;
082    
083        /**
084         * The working directory.
085         *
086         * @parameter expression="${workingDirectory}"
087         */
088        private File workingDirectory;
089    
090        /**
091         * The user name (used by svn, starteam and perforce protocol).
092         *
093         * @parameter expression="${username}"
094         */
095        private String username;
096    
097        /**
098         * The user password (used by svn, starteam and perforce protocol).
099         *
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    }