View Javadoc
1   package org.codehaus.mojo.siteskinner;
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.FileInputStream;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.text.SimpleDateFormat;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.Date;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.Properties;
34  import java.util.StringTokenizer;
35  import java.util.jar.JarEntry;
36  import java.util.jar.JarFile;
37  
38  import org.apache.commons.cli.CommandLine;
39  import org.apache.commons.cli.ParseException;
40  import org.apache.maven.artifact.Artifact;
41  import org.apache.maven.artifact.factory.ArtifactFactory;
42  import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
43  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
44  import org.apache.maven.artifact.repository.ArtifactRepository;
45  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
46  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
47  import org.apache.maven.artifact.resolver.ArtifactResolver;
48  import org.apache.maven.artifact.versioning.ArtifactVersion;
49  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
50  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
51  import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
52  import org.apache.maven.artifact.versioning.VersionRange;
53  import org.apache.maven.doxia.site.decoration.Body;
54  import org.apache.maven.doxia.site.decoration.DecorationModel;
55  import org.apache.maven.doxia.site.decoration.PublishDate;
56  import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader;
57  import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer;
58  import org.apache.maven.doxia.tools.SiteTool;
59  import org.apache.maven.doxia.tools.SiteToolException;
60  import org.apache.maven.model.Plugin;
61  import org.apache.maven.plugin.AbstractMojo;
62  import org.apache.maven.plugin.MojoExecutionException;
63  import org.apache.maven.plugin.MojoFailureException;
64  import org.apache.maven.plugins.annotations.Component;
65  import org.apache.maven.plugins.annotations.Mojo;
66  import org.apache.maven.plugins.annotations.Parameter;
67  import org.apache.maven.project.MavenProject;
68  import org.apache.maven.project.MavenProjectBuilder;
69  import org.apache.maven.project.ProjectBuildingException;
70  import org.apache.maven.scm.manager.ScmManager;
71  import org.apache.maven.shared.invoker.DefaultInvocationRequest;
72  import org.apache.maven.shared.invoker.InvocationRequest;
73  import org.apache.maven.shared.invoker.InvocationResult;
74  import org.apache.maven.shared.invoker.Invoker;
75  import org.apache.maven.shared.invoker.InvokerLogger;
76  import org.apache.maven.shared.invoker.MavenInvocationException;
77  import org.codehaus.plexus.util.FileUtils;
78  import org.codehaus.plexus.util.IOUtil;
79  import org.codehaus.plexus.util.ReaderFactory;
80  import org.codehaus.plexus.util.StringUtils;
81  import org.codehaus.plexus.util.WriterFactory;
82  import org.codehaus.plexus.util.cli.CommandLineUtils;
83  import org.codehaus.plexus.util.xml.Xpp3Dom;
84  import org.codehaus.plexus.util.xml.Xpp3DomUtils;
85  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
86  
87  /**
88   * Call <code>mvn siteskinner:skin</code> on a maven project. This will check out the latest releases project. Next it
89   * will add/replace the skin of the site.xml with the skin of the current project. Finally it will invoke a
90   * <code>mvn site</code> on the checked out project. Now you can verify the pages and run <code>mvn site:deploy</code>
91   * on the checked out project.
92   */
93  @Mojo( name = "skin", requiresDirectInvocation = true, aggregator = true )
94  public class SkinMojo
95      extends AbstractMojo
96  {
97      private static final String MAVEN_SITE_PLUGIN_KEY = "org.apache.maven.plugins:maven-site-plugin";
98  
99      /**
100      * Some versions of the maven-site-plugin require a specific Maven version. This check is done by the siteskinner.
101      * You can fork the execution of the site generation to another version of maven by setting the {@code mavenHome}
102      * <table>
103      * <tr>
104      * <th>Maven Site Plugin version</th>
105      * <th>Required Maven version</th>
106      * </tr>
107      * <tr>
108      * <td>(,3.0-alpha-1)</td>
109      * <td>2.x</td>
110      * </tr>
111      * <tr>
112      * <td>[3.0-alpha-1,3.0)</td>
113      * <td>3.x</td>
114      * </tr>
115      * <tr>
116      * <td>[3.0,)</td>
117      * <td>2.x or 3.x</td>
118      * </tr>
119      * </table>
120      * @since 1.1
121      */
122     @Parameter( property = "mavenHome" )
123     private File mavenHome;
124 
125     /**
126      * Addition arguments, accepts:
127      * <ul>
128      *   <li>-D,--define &lt;arg&gt;</li>
129      *   <li>-P,--activate-profiles &lt;arg&gt;</li>
130      *   <li>-X,--debug</li>
131      * </ul>
132      * @since 1.1
133      */
134     @Parameter( property = "arguments" )
135     private String arguments;
136 
137     /**
138      * Force a checkout instead of an update when the sources have already been checked out during a previous run.
139      * @since 1.0
140      */
141     @Parameter( property = "forceCheckout", defaultValue = "false" )
142     private boolean forceCheckout;
143 
144     /**
145      * If {@code true}, all the elements of the body in the {@code site.xml} will be merged, except the menu items. Set
146      * to {@false} if you don't want to merge the body.
147      * @since 1.0
148      */
149     @Parameter( property = "mergeBody", defaultValue = "true" )
150     private boolean mergeBody;
151 
152     /**
153      * If {@code false} the plugin should only generate the site, else if {@code true} the site should be published
154      * immediately too.
155      * @since 1.0
156      */
157     @Parameter( property = "siteDeploy", defaultValue = "false" )
158     private boolean siteDeploy;
159 
160     /**
161      * In most cases this plugin can discover the original publishDate. You could set this value for those cases when this fails
162      * @since 1.1
163      */
164     @Parameter( property = "siteskinner.publishDate" )
165     private String publishDate;
166     
167     /**
168      * Specifies the input encoding.
169      * @since 1.0
170      */
171     @Parameter( defaultValue = "${project.build.sourceEncoding}", property = "encoding" )
172     private String inputEncoding;
173 
174     /**
175      * Specifies the output encoding.
176      * @since 1.0
177      */
178     @Parameter( defaultValue = "${project.reporting.outputEncoding}", property = "outputEncoding" )
179     private String outputEncoding;
180 
181     /* Read-only parmaters */
182     
183     /**
184      * Versionrange to calculate latest released version
185      */
186     @Parameter( defaultValue = "(,${project.version})", readonly = true )
187     private String releasedVersion;
188 
189     /**
190      * The working directory for this plugin.
191      */
192     @Parameter( defaultValue = "${project.build.directory}/siteskinner", readonly = true )
193     private File workingDirectory;
194 
195     /**
196      * The reactor projects.
197      */
198     @Parameter( defaultValue = "${reactorProjects}", readonly = true, required = true )
199     private List<MavenProject> reactorProjects;
200 
201     /**
202      * Gets the input files encoding.
203      * 
204      * @return The input files encoding, never <code>null</code>.
205      */
206     private String getInputEncoding()
207     {
208         return ( inputEncoding == null ) ? ReaderFactory.ISO_8859_1 : inputEncoding;
209     }
210 
211     /**
212      * Gets the effective reporting output files encoding.
213      * 
214      * @return The effective reporting output file encoding, never <code>null</code>.
215      */
216     private String getOutputEncoding()
217     {
218         return ( outputEncoding == null ) ? WriterFactory.UTF_8 : outputEncoding;
219     }
220 
221     @Component
222     private MavenProject currentProject;
223 
224     /**
225      * The local repository where the artifacts are located.
226      */
227     @Parameter( defaultValue = "${localRepository}", readonly = true, required = true )
228     private ArtifactRepository localRepository;
229 
230     /**
231      * @since 1.0
232      */
233     @Parameter( property = "settingsFile" )
234     private File settingsFile;
235 
236     /**
237      * The remote repositories where artifacts are located.
238      */
239     @Parameter( defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true )
240     private List<ArtifactRepository> remoteRepositories;
241 
242     @Component
243     private MavenProjectBuilder mavenProjectBuilder;
244 
245     @Component
246     private ScmManager scmManager;
247 
248     @Component
249     private ArtifactMetadataSource metadataSource;
250 
251     @Component
252     private ArtifactFactory factory;
253 
254     @Component
255     private ArtifactResolver resolver;
256 
257     @Component
258     private SiteTool siteTool;
259 
260     @Component
261     private Invoker invoker;
262 
263     /** {@inheritDoc} */
264     public void execute()
265         throws MojoExecutionException, MojoFailureException
266     {
267         MavenProject releasedProject;
268         Artifact releasedArtifact;
269         try
270         {
271             releasedArtifact = resolveArtifact( releasedVersion );
272             MavenProject externalProject =
273                 mavenProjectBuilder.buildFromRepository( releasedArtifact, remoteRepositories, localRepository );
274 
275             fetchSources( workingDirectory, externalProject );
276 
277             releasedProject =
278                 mavenProjectBuilder.build( new File( workingDirectory, "pom.xml" ), localRepository, null );
279         }
280         catch ( ProjectBuildingException e )
281         {
282             throw new MojoExecutionException( e.getMessage() );
283         }
284 
285         verifyVersionCompatibility( releasedProject );
286 
287         Xpp3Dom releasedConfig = getSitePluginConfiguration( releasedProject );
288         String releasedSiteDirectory =
289             releasedConfig == null || releasedConfig.getChild( "siteDirectory" ) == null ? "src/site"
290                             : releasedConfig.getChild( "siteDirectory" ).getValue();
291         String releasedLocales =
292             ( releasedConfig == null || releasedConfig.getChild( "locales" ) == null ? null
293                             : releasedConfig.getChild( "locales" ).getValue() );
294 
295         Xpp3Dom currentConfig = getSitePluginConfiguration( currentProject );
296         String currentSiteDirectory =
297             currentConfig == null || currentConfig.getChild( "siteDirectory" ) == null ? "src/site"
298                             : currentConfig.getChild( "siteDirectory" ).getValue();
299         try
300         {
301             DecorationXpp3Writer writer = new DecorationXpp3Writer();
302             DecorationXpp3Reader reader = new DecorationXpp3Reader();
303 
304             for ( Locale locale : siteTool.getAvailableLocales( releasedLocales ) )
305             {
306                 DecorationModel resolvedCurrentModel;
307                 try
308                 {
309                     resolvedCurrentModel =
310                         siteTool.getDecorationModel( currentProject, reactorProjects, localRepository,
311                                                      remoteRepositories, currentSiteDirectory, locale,
312                                                      getInputEncoding(), getOutputEncoding() );
313                 }
314                 catch ( SiteToolException e )
315                 {
316                     getLog().warn( e.getMessage(), e );
317                     continue;
318                 }
319 
320                 if ( resolvedCurrentModel.getSkin() == null )
321                 {
322                     throw new MojoFailureException(
323                                                     "No skin defined in the current project, neither inherited; Can't apply a new skin on the old site." );
324                 }
325 
326                 File currentSiteXml =
327                     siteTool.getSiteDescriptorFromBasedir( currentSiteDirectory, currentProject.getBasedir(), locale );
328 
329                 DecorationModel currentModel;
330                 if ( currentSiteXml.exists() )
331                 {
332                     currentModel = readDecorationModel( reader, currentSiteXml );
333                 }
334                 else
335                 {
336                     currentModel = new DecorationModel();
337                 }
338 
339                 File releasedSiteXml =
340                     siteTool.getSiteDescriptorFromBasedir( releasedSiteDirectory, releasedProject.getBasedir(), locale );
341 
342                 DecorationModel releasedModel = null;
343                 if ( releasedSiteXml.exists() )
344                 {
345                     releasedModel = readDecorationModel( reader, releasedSiteXml );
346                 }
347                 else
348                 {
349                     // already create folders to be sure we can write to this file
350                     releasedSiteXml.getParentFile().mkdirs();
351                     releasedModel = new DecorationModel();
352                 }
353 
354                 releasedModel.setSkin( resolvedCurrentModel.getSkin() );
355                 // MOJO-1827: Copy all layout-specific content
356                 releasedModel.setBannerLeft( currentModel.getBannerLeft() );
357                 releasedModel.setBannerRight( currentModel.getBannerRight() );
358                 releasedModel.setGoogleAnalyticsAccountId( currentModel.getGoogleAnalyticsAccountId() );
359                 releasedModel.setModelEncoding( currentModel.getModelEncoding() );
360                 releasedModel.setName( currentModel.getName() );
361                 releasedModel.setPoweredBy( currentModel.getPoweredBy() );
362                 releasedModel.setPublishDate( currentModel.getPublishDate() );
363                 releasedModel.setVersion( currentModel.getVersion() );
364 
365                 if ( mergeBody && currentModel.getBody() != null )
366                 {
367                     if ( releasedModel.getBody() == null )
368                     {
369                         releasedModel.setBody( new Body() );
370                     }
371                     releasedModel.getBody().setBreadcrumbs( currentModel.getBody().getBreadcrumbs() );
372                     releasedModel.getBody().setFooter( currentModel.getBody().getFooter() );
373                     releasedModel.getBody().setHead( currentModel.getBody().getHead() );
374                     releasedModel.getBody().setLinks( currentModel.getBody().getLinks() );
375                 }
376 
377                 Xpp3Dom mergedCustom =
378                     Xpp3DomUtils.mergeXpp3Dom( (Xpp3Dom) currentModel.getCustom(), (Xpp3Dom) releasedModel.getCustom() );
379 
380                 if ( mergedCustom == null )
381                 {
382                     mergedCustom = new Xpp3Dom( "custom" );
383                 }
384 
385                 String publishDateFormat;
386                 if ( releasedModel.getPublishDate() != null )
387                 {
388                     publishDateFormat = releasedModel.getPublishDate().getFormat();
389                 }
390                 else
391                 {
392                     publishDateFormat = new PublishDate().getFormat();
393                 }
394 
395                 Xpp3Dom publishDateChild = new Xpp3Dom( "publishDate" );
396                 
397                 String publishDateValue;
398                 
399                 if ( publishDate == null )
400                 {
401                     long preResolveDate = System.currentTimeMillis();
402 
403                     try
404                     {
405                         resolver.resolveAlways( releasedArtifact, remoteRepositories, localRepository );
406                     }
407                     catch ( ArtifactResolutionException e )
408                     {
409                         throw new MojoExecutionException( e.getMessage() );
410                     }
411                     catch ( ArtifactNotFoundException e )
412                     {
413                         throw new MojoExecutionException( e.getMessage() );
414                     }
415 
416                     long deployDate;
417                     if ( releasedArtifact.getFile().lastModified() < preResolveDate )
418                     {
419                         // we can assume that the ArtifactResolver changed the lastModified value
420                         deployDate = releasedArtifact.getFile().lastModified();
421                     }
422                     else
423                     {
424                         // Use the modified-date from the first entry of the jar as releaseDate
425                         JarFile jarFile = new JarFile( releasedArtifact.getFile() );
426                         JarEntry entry = jarFile.entries().nextElement();
427 
428                         deployDate = entry.getTime();
429                     }
430                     Date releaseDate = new Date( deployDate );
431                     
432                     publishDateValue = new SimpleDateFormat( publishDateFormat ).format( releaseDate );
433                 }
434                 else
435                 {
436                     // verify that specified publishDate matches the publishDateFormat
437                     try
438                     {
439                         new SimpleDateFormat( publishDateFormat ).parse( publishDate );
440                     }
441                     catch ( java.text.ParseException e )
442                     {
443                         throw new MojoExecutionException( e.getMessage() );
444                     }
445                     publishDateValue = publishDate;
446                 }
447 
448                 publishDateChild.setValue( publishDateValue );
449                 mergedCustom.addChild( publishDateChild );
450                 releasedModel.setCustom( mergedCustom );
451 
452                 FileOutputStream fileOutputStream = new FileOutputStream( releasedSiteXml );
453                 try
454                 {
455                     writer.write( fileOutputStream, releasedModel );
456                 }
457                 finally
458                 {
459                     IOUtil.close( fileOutputStream );
460                 }
461 
462             }
463         }
464         catch ( IOException e )
465         {
466             throw new MojoExecutionException( e.getMessage() );
467         }
468         catch ( XmlPullParserException e )
469         {
470             throw new MojoExecutionException( e.getMessage() );
471         }
472 
473         InvocationRequest request = buildInvokerRequest( releasedProject );
474 
475         invoker.setLocalRepositoryDirectory( new File( localRepository.getBasedir() ) );
476         invoker.setMavenHome( mavenHome );
477 
478         if ( getLog().isDebugEnabled() )
479         {
480             invoker.getLogger().setThreshold( InvokerLogger.DEBUG );
481         }
482         else if ( getLog().isInfoEnabled() )
483         {
484             invoker.getLogger().setThreshold( InvokerLogger.INFO );
485         }
486         else if ( getLog().isWarnEnabled() )
487         {
488             invoker.getLogger().setThreshold( InvokerLogger.WARN );
489         }
490         else if ( getLog().isErrorEnabled() )
491         {
492             invoker.getLogger().setThreshold( InvokerLogger.ERROR );
493         }
494         
495         try
496         {
497             InvocationResult invocationResult = invoker.execute( request );
498             if ( invocationResult.getExitCode() != 0 )
499             {
500                 throw new MojoExecutionException( invocationResult.getExecutionException().getMessage() );
501             }
502         }
503         catch ( MavenInvocationException e )
504         {
505             throw new MojoExecutionException( e.getMessage() );
506         }
507     }
508 
509     private InvocationRequest buildInvokerRequest( MavenProject releasedProject )
510         throws MojoFailureException
511     {
512         InvocationRequest request = new DefaultInvocationRequest();
513         request.setGoals( Collections.singletonList( siteDeploy ? "site-deploy" : "site" ) );
514         request.setPomFile( releasedProject.getFile() );
515         request.setShowErrors( true );
516         request.setUserSettingsFile( settingsFile );
517 
518         if ( arguments != null )
519         {
520             try
521             {
522                 String[] args = CommandLineUtils.translateCommandline( arguments );
523                 CLIManager cliManager = new CLIManager();
524                 CommandLine cl = cliManager.parse( args );
525                 request.setDebug( cl.hasOption( CLIManager.DEBUG ) );
526 
527                 // ----------------------------------------------------------------------
528                 // Profile Activation
529                 // ----------------------------------------------------------------------
530                 List<String> profiles = new ArrayList<String>();
531 
532                 if ( cl.hasOption( CLIManager.ACTIVATE_PROFILES ) )
533                 {
534                     String[] profileOptionValues = cl.getOptionValues( CLIManager.ACTIVATE_PROFILES );
535                     if ( profileOptionValues != null )
536                     {
537                         for ( String profileOptionValue : profileOptionValues )
538                         {
539                             StringTokenizer profileTokens = new StringTokenizer( profileOptionValue, "," );
540 
541                             while ( profileTokens.hasMoreTokens() )
542                             {
543                                 profiles.add( profileTokens.nextToken().trim() );
544                             }
545                         }
546                     }
547                     request.setProfiles( profiles );
548                 }
549 
550                 if ( cl.hasOption( CLIManager.SET_SYSTEM_PROPERTY ) )
551                 {
552                     Properties userProperties = new Properties();
553                     String[] defStrs = cl.getOptionValues( CLIManager.SET_SYSTEM_PROPERTY );
554         
555                     if ( defStrs != null )
556                     {
557                         for ( String defStr : defStrs )
558                         {
559                             setCliProperty( defStr, userProperties );
560                         }
561                     }
562                     request.setProperties( userProperties );
563                 }
564             }
565             catch ( ParseException e )
566             {
567                 throw new MojoFailureException( "Unsupported option: " + e.getMessage() );
568             }
569             catch ( Exception e )
570             {
571                 throw new MojoFailureException( e.getMessage() );
572             }
573         }
574         return request;
575     }
576 
577     private DecorationModel readDecorationModel( DecorationXpp3Reader reader, File currentSiteXml )
578         throws IOException, XmlPullParserException
579     {
580         DecorationModel currentModel;
581         FileInputStream fileInputStream = new FileInputStream( currentSiteXml );
582         try
583         {
584             currentModel = reader.read( fileInputStream, false );
585         }
586         finally
587         {
588             IOUtil.close( fileInputStream );
589         }
590         return currentModel;
591     }
592 
593     /**
594      * Verify is the Maven version can be used with the specified maven-site-plugin version, since the maven-site-plugin
595      * is not compatible with every Maven version.
596      * 
597      * @param releasedProject
598      * @throws MojoFailureException
599      */
600     private void verifyVersionCompatibility( MavenProject releasedProject )
601         throws MojoFailureException
602     {
603         // MOJO-1825: verify site-plugin-version with maven-version
604         ArtifactVersion sitePluginVersion = getSitePluginVersion( releasedProject );
605 
606         String mavenVersion;
607         if ( mavenHome == null )
608         {
609             mavenVersion = SelectorUtils.getMavenVersion();
610         }
611         else
612         {
613             mavenVersion = SelectorUtils.getMavenVersion( mavenHome );
614         }
615 
616         if ( sitePluginVersion != null )
617         {
618             try
619             {
620                 if ( VersionRange.createFromVersionSpec( "(,3.0-alpha-1)" ).containsVersion( sitePluginVersion )
621                     && VersionRange.createFromVersionSpec( "[3.0,)" ).containsVersion( new DefaultArtifactVersion(
622                                                                                                                    mavenVersion ) ) )
623                 {
624                     throw new MojoFailureException( "maven-site-plugin:" + sitePluginVersion
625                         + " can only be executed with Maven 2.x" );
626                 }
627                 else if ( VersionRange.createFromVersionSpec( "[3.0-alpha-1,3.0)" ).containsVersion( sitePluginVersion )
628                     && VersionRange.createFromVersionSpec( "(, 3.0)" ).containsVersion( new DefaultArtifactVersion(
629                                                                                                                     mavenVersion ) ) )
630                 {
631                     throw new MojoFailureException( "maven-site-plugin:" + sitePluginVersion
632                         + " can only be executed with Maven 3.x+" );
633                 }
634             }
635             catch ( InvalidVersionSpecificationException e )
636             {
637                 throw new MojoFailureException( e.getMessage() );
638             }
639         }
640     }
641 
642     private Xpp3Dom getSitePluginConfiguration( MavenProject releasedProject )
643     {
644         Plugin sitePlugin = (Plugin) releasedProject.getBuild().getPluginsAsMap().get( MAVEN_SITE_PLUGIN_KEY );
645         if ( sitePlugin == null )
646         {
647             sitePlugin =
648                 (Plugin) releasedProject.getBuild().getPluginManagement().getPluginsAsMap().get( MAVEN_SITE_PLUGIN_KEY );
649         }
650         return (Xpp3Dom) sitePlugin.getConfiguration();
651     }
652 
653     private ArtifactVersion getSitePluginVersion( MavenProject releasedProject )
654     {
655         ArtifactVersion sitePluginVersion = null;
656         Plugin sitePlugin = (Plugin) releasedProject.getBuild().getPluginsAsMap().get( MAVEN_SITE_PLUGIN_KEY );
657         if ( sitePlugin == null )
658         {
659             sitePlugin =
660                 (Plugin) releasedProject.getBuild().getPluginManagement().getPluginsAsMap().get( MAVEN_SITE_PLUGIN_KEY );
661         }
662 
663         if ( sitePlugin != null && sitePlugin.getVersion() != null )
664         {
665             sitePluginVersion = new DefaultArtifactVersion( sitePlugin.getVersion() );
666         }
667         return sitePluginVersion;
668     }
669 
670     private String getConnection( MavenProject mavenProject )
671         throws MojoFailureException
672     {
673         if ( mavenProject.getScm() == null )
674         {
675             throw new MojoFailureException( "SCM is not set in your pom.xml." );
676         }
677 
678         String connection = mavenProject.getScm().getConnection();
679 
680         if ( connection != null )
681         {
682             if ( connection.length() > 0 )
683             {
684                 return connection;
685             }
686         }
687         connection = mavenProject.getScm().getDeveloperConnection();
688 
689         if ( StringUtils.isEmpty( connection ) )
690         {
691             throw new MojoFailureException( "SCM Connection is not set in your pom.xml." );
692         }
693         return connection;
694     }
695 
696     private Artifact resolveArtifact( String versionSpec )
697         throws MojoFailureException, MojoExecutionException
698     {
699         // Find the previous version JAR and resolve it, and it's dependencies
700         VersionRange range;
701         try
702         {
703             range = VersionRange.createFromVersionSpec( versionSpec );
704         }
705         catch ( InvalidVersionSpecificationException e )
706         {
707             throw new MojoFailureException( "Invalid comparison version: " + e.getMessage() );
708         }
709 
710         Artifact previousArtifact;
711         try
712         {
713             previousArtifact =
714                 factory.createDependencyArtifact( currentProject.getGroupId(), currentProject.getArtifactId(), range,
715                                                   currentProject.getPackaging(), null, Artifact.SCOPE_COMPILE );
716 
717             if ( !previousArtifact.getVersionRange().isSelectedVersionKnown( previousArtifact ) )
718             {
719                 getLog().debug( "Searching for versions in range: " + previousArtifact.getVersionRange() );
720                 List<ArtifactVersion> availableVersions =
721                     metadataSource.retrieveAvailableVersions( previousArtifact, localRepository,
722                                                               currentProject.getRemoteArtifactRepositories() );
723                 filterSnapshots( availableVersions );
724                 ArtifactVersion version = range.matchVersion( availableVersions );
725                 if ( version != null )
726                 {
727                     previousArtifact.selectVersion( version.toString() );
728                 }
729             }
730 
731         }
732         catch ( OverConstrainedVersionException e1 )
733         {
734             throw new MojoFailureException( "Invalid comparison version: " + e1.getMessage() );
735         }
736         catch ( ArtifactMetadataRetrievalException e11 )
737         {
738             throw new MojoExecutionException( "Error determining previous version: " + e11.getMessage(), e11 );
739         }
740 
741         if ( previousArtifact.getVersion() == null )
742         {
743             getLog().info( "Unable to find a previous version of the project in the repository" );
744         }
745         else
746         {
747             getLog().debug( "Previous version: " + previousArtifact.getVersion() );
748         }
749 
750         return previousArtifact;
751     }
752 
753     private void filterSnapshots( List<ArtifactVersion> versions )
754     {
755         for ( Iterator<ArtifactVersion> versionIterator = versions.iterator(); versionIterator.hasNext(); )
756         {
757             if ( "SNAPSHOT".equals( versionIterator.next().getQualifier() ) )
758             {
759                 versionIterator.remove();
760             }
761         }
762     }
763 
764     private void fetchSources( File checkoutDir, MavenProject mavenProject )
765         throws MojoExecutionException
766     {
767         try
768         {
769             if ( forceCheckout && checkoutDir.exists() )
770             {
771                 FileUtils.deleteDirectory( checkoutDir );
772             }
773 
774             if ( checkoutDir.mkdirs() )
775             {
776 
777                 getLog().info( "Performing checkout to " + checkoutDir );
778 
779                 new ScmCommandExecutor( scmManager, getConnection( mavenProject ), getLog() ).checkout( checkoutDir.getPath() );
780             }
781             else
782             {
783                 getLog().info( "Performing update to " + checkoutDir );
784 
785                 new ScmCommandExecutor( scmManager, getConnection( mavenProject ), getLog() ).update( checkoutDir.getPath() );
786             }
787         }
788         catch ( Exception ex )
789         {
790             throw new MojoExecutionException( "checkout failed.", ex );
791         }
792     }
793     
794     private static void setCliProperty( String property, Properties properties )
795     {
796         String name;
797 
798         String value;
799 
800         int i = property.indexOf( "=" );
801 
802         if ( i <= 0 )
803         {
804             name = property.trim();
805 
806             value = "true";
807         }
808         else
809         {
810             name = property.substring( 0, i ).trim();
811 
812             value = property.substring( i + 1 );
813         }
814 
815         properties.setProperty( name, value );
816     }
817 }