Checkpointing progress

git-svn-id: https://svn.apache.org/repos/asf/maven/sandbox/trunk@1502379 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/plugins/maven-downloads-plugin/pom.xml b/plugins/maven-downloads-plugin/pom.xml
new file mode 100644
index 0000000..6018535
--- /dev/null
+++ b/plugins/maven-downloads-plugin/pom.xml
@@ -0,0 +1,448 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>maven-plugins</artifactId>
+    <groupId>org.apache.maven.plugins</groupId>
+    <version>24</version>
+    <relativePath>../maven-plugins/pom.xml</relativePath>
+  </parent>
+
+  <artifactId>maven-download-links-plugin</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>maven-plugin</packaging>
+
+  <name>Maven Download Links Plugin</name>
+  <description>
+    The Maven Download Links Plugin is a plugin that generates a download links page report
+    for the specified project with links to download various distributions.
+  </description>
+  <inceptionYear>2013</inceptionYear>
+
+  <prerequisites>
+    <maven>${mavenVersion}</maven>
+  </prerequisites>
+
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/maven/sandbox/trunk/plugins/maven-downloads-plugin/</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/maven/sandbox/trunk/plugins/maven-downloads-plugin/</developerConnection>
+    <url>http://svn.apache.org/viewvc/maven/sandbox/trunk/plugins/maven-downloads-plugin/</url>
+  </scm>
+  <distributionManagement>
+    <site>
+      <id>apache.website</id>
+      <url>scm:svn:https://svn.apache.org/repos/infra/websites/production/maven/content/${maven.site.path}</url>
+    </site>
+  </distributionManagement>
+
+  <properties>
+    <doxiaVersion>1.4</doxiaVersion>
+    <doxia-sitetoolsVersion>1.2</doxia-sitetoolsVersion>
+    <mavenVersion>2.2.1</mavenVersion>
+    <wagonVersion>1.0</wagonVersion>
+    <scmVersion>1.8</scmVersion>
+    <sitePluginVersion>3.2</sitePluginVersion>
+    <mavenPluginVersion>3.2</mavenPluginVersion>
+    <maven.site.path>plugins-archives/${project.artifactId}-LATEST</maven.site.path>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.reporting</groupId>
+      <artifactId>maven-reporting-api</artifactId>
+      <version>3.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.reporting</groupId>
+      <artifactId>maven-reporting-impl</artifactId>
+      <version>2.1</version>
+    </dependency>
+
+    <!-- Maven -->
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-artifact</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-artifact-manager</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-model</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-project</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-repository-metadata</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-settings</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-dependency-tree</artifactId>
+      <version>2.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-doxia-tools</artifactId>
+      <version>1.4</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-shared-jar</artifactId>
+      <version>1.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.maven.plugin-tools</groupId>
+      <artifactId>maven-plugin-annotations</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- Wagon -->
+    <dependency>
+      <groupId>org.apache.maven.wagon</groupId>
+      <artifactId>wagon-provider-api</artifactId>
+      <version>${wagonVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.wagon</groupId>
+      <artifactId>wagon-ssh</artifactId>
+      <version>${wagonVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.wagon</groupId>
+      <artifactId>wagon-file</artifactId>
+      <version>${wagonVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.wagon</groupId>
+      <artifactId>wagon-http-lightweight</artifactId>
+      <version>${wagonVersion}</version>
+    </dependency>
+
+    <!-- Doxia -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-sink-api</artifactId>
+      <version>${doxiaVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-logging-api</artifactId>
+      <version>${doxiaVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-core</artifactId>
+      <version>${doxiaVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-module-xhtml</artifactId>
+      <version>${doxiaVersion}</version>
+    </dependency>
+
+    <!-- Doxia-sitetools -->
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-decoration-model</artifactId>
+      <version>${doxia-sitetoolsVersion}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.doxia</groupId>
+      <artifactId>doxia-site-renderer</artifactId>
+      <version>${doxia-sitetoolsVersion}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-container-default</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-component-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <!-- Plexus -->
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-container-default</artifactId>
+      <version>1.0-alpha-9</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-i18n</artifactId>
+      <version>1.0-beta-7</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-component-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+      <version>3.0.8</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-interpolation</artifactId>
+      <version>1.9</version>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-validator</groupId>
+      <artifactId>commons-validator</artifactId>
+      <version>1.4.0</version>
+    </dependency>
+    <dependency>
+      <groupId>joda-time</groupId>
+      <artifactId>joda-time</artifactId>
+      <version>1.6.2</version>
+    </dependency>
+
+    <!-- test -->
+    <dependency>
+      <groupId>httpunit</groupId>
+      <artifactId>httpunit</artifactId>
+      <version>1.6</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.plugin-testing</groupId>
+      <artifactId>maven-plugin-testing-harness</artifactId>
+      <version>1.2</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit-addons</groupId>
+      <artifactId>junit-addons</artifactId>
+      <version>1.4</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mortbay.jetty</groupId>
+      <artifactId>jetty</artifactId>
+      <version>6.1.5</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-jarsigner-plugin</artifactId>
+          <version>1.2</version>
+          <configuration>
+            <keystore>${basedir}/target/jetty.jks</keystore>
+            <keypass>apache</keypass>
+            <storepass>apache</storepass>
+            <alias>apache</alias>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-plugin-plugin</artifactId>
+          <version>${mavenPluginVersion}</version>
+          <configuration>
+            <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>keytool-maven-plugin</artifactId>
+          <version>1.0</version>
+          <configuration>
+            <keystore>${basedir}/target/jetty.jks</keystore>
+            <dname>cn=jetty, ou=jetty, L=Unknown, ST=Unknown, o=Apache, c=Unknown</dname>
+            <keypass>apache</keypass>
+            <storepass>apache</storepass>
+            <alias>apache</alias>
+            <validity>365</validity>
+            <keyalg>RSA</keyalg>
+          </configuration>
+        </plugin>
+        <plugin>
+          <artifactId>maven-release-plugin</artifactId>
+          <configuration>
+            <releaseProfiles>apache-release,rat,run-its</releaseProfiles><!-- TODO add to parent pom -->
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>**/DependenciesReportTest*</exclude><!-- FIXME: missing Maven 3 class or conflict -->
+          </excludes>
+        </configuration>
+      </plugin>
+      <!-- To use jetty ssl in tests -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jarsigner-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>sign</id>
+            <goals>
+              <goal>sign</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>verify</id>
+            <goals>
+              <goal>verify</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>mojo-descriptor</id>
+            <goals>
+              <goal>descriptor</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>keytool-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>init</id>
+            <phase>initialize</phase>
+            <goals>
+              <goal>clean</goal>
+              <goal>genkey</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <version>${mavenPluginVersion}</version>
+        <configuration>
+          <requirements>
+            <jdk>1.5</jdk>
+            <others>
+              <property>
+                <name>Maven Site Plugin</name>
+                <value>3.0</value>
+              </property>
+            </others>
+          </requirements>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-download-links-plugin</artifactId>
+        <configuration>
+
+        </configuration>
+        <reportSets>
+          <reportSet>
+            <reports><report>report</report></reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+    </plugins>
+  </reporting>
+
+  <profiles>
+    <profile>
+      <id>reporting</id>
+      <reporting>
+        <plugins>
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>l10n-maven-plugin</artifactId>
+            <version>1.0-alpha-2</version>
+            <configuration>
+              <locales>
+              </locales>
+            </configuration>
+          </plugin>
+        </plugins>
+      </reporting>
+    </profile>
+    <profile>
+      <id>run-its</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-invoker-plugin</artifactId>
+            <configuration>
+              <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
+              <pomIncludes>
+                <pomInclude>*/pom.xml</pomInclude>
+              </pomIncludes>
+              <pomExcludes>
+                <pomExclude>pom.xml</pomExclude>
+              </pomExcludes>
+              <localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
+              <goals>
+                <goal>site</goal>
+              </goals>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
diff --git a/plugins/maven-downloads-plugin/src/main/java/org/apache/maven/report/downloads/DownloadLinksRenderer.java b/plugins/maven-downloads-plugin/src/main/java/org/apache/maven/report/downloads/DownloadLinksRenderer.java
new file mode 100644
index 0000000..73dbd94
--- /dev/null
+++ b/plugins/maven-downloads-plugin/src/main/java/org/apache/maven/report/downloads/DownloadLinksRenderer.java
@@ -0,0 +1,117 @@
+package org.apache.maven.report.downloads;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.reporting.AbstractMavenReportRenderer;
+import org.codehaus.plexus.i18n.I18N;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+public class DownloadLinksRenderer
+    extends AbstractMavenReportRenderer
+{
+    protected I18N i18n;
+
+    protected Locale locale;
+
+    private String name;
+
+    public DownloadLinksRenderer( Sink sink, I18N i18n, Locale locale )
+    {
+        super( sink );
+
+        this.i18n = i18n;
+
+        this.locale = locale;
+    }
+
+    @Override
+    public String getTitle()
+    {
+        return format( "download-links", "title", getName() ) ;
+    }
+
+    protected String getI18nString( String key )
+    {
+        return getI18nString( "download-links", key );
+    }
+
+    protected String getI18nString( String section, String key )
+    {
+        return i18n.getString( "download-links-report", locale, "report." + section + '.' + key );
+    }
+
+    protected String format( String section, String key, Object arg1 )
+    {
+        return i18n.format( "download-links-report", locale, "report." + section + '.' + key, arg1 );
+    }
+
+    protected String format( String section, String key, Object arg1, Object arg2 )
+    {
+        return i18n.format( "download-links-report", locale, "report." + section + '.' + key, arg1, arg2 );
+    }
+
+    protected String format( String section, String key, Object[] args )
+    {
+        return i18n.format( "download-links-report", locale, "report." + section + '.' + key, args );
+    }
+
+    @Override
+    protected void text( String text )
+    {
+        if ( StringUtils.isEmpty( text ) ) // Take care of spaces
+        {
+            sink.text( "-" );
+        }
+        else
+        {
+            // custombundle text with xml?
+            String regex = "(.+?)<(\"[^\"]*\"|'[^']*'|[^'\">])*>(.+?)";
+            if ( Pattern.matches( regex, text ) )
+            {
+                sink.rawText( text );
+            }
+            else
+            {
+                sink.text( text );
+            }
+        }
+    }
+
+    @Override
+    protected void renderBody()
+    {
+        startSection( getTitle() );
+    }
+
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+}
diff --git a/plugins/maven-downloads-plugin/src/main/java/org/apache/maven/report/downloads/DownloadLinksReport.java b/plugins/maven-downloads-plugin/src/main/java/org/apache/maven/report/downloads/DownloadLinksReport.java
new file mode 100644
index 0000000..6412eba
--- /dev/null
+++ b/plugins/maven-downloads-plugin/src/main/java/org/apache/maven/report/downloads/DownloadLinksReport.java
@@ -0,0 +1,564 @@
+package org.apache.maven.report.downloads;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactResolver;
+import org.apache.maven.doxia.sink.render.RenderingContext;
+import org.apache.maven.doxia.site.decoration.Body;
+import org.apache.maven.doxia.site.decoration.DecorationModel;
+import org.apache.maven.doxia.siterenderer.Renderer;
+import org.apache.maven.doxia.siterenderer.RendererException;
+import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
+import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
+import org.apache.maven.doxia.tools.SiteTool;
+import org.apache.maven.doxia.tools.SiteToolException;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.reporting.AbstractMavenReport;
+import org.apache.maven.reporting.MavenReportException;
+import org.apache.maven.settings.Settings;
+import org.codehaus.plexus.i18n.I18N;
+import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
+import org.codehaus.plexus.interpolation.InterpolationException;
+import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
+import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
+import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * @author stephenc
+ * @since 08/07/2013 23:53
+ */
+@Mojo( name = "report" )
+public class DownloadLinksReport
+        extends AbstractMavenReport
+{
+    // ----------------------------------------------------------------------
+    // Mojo components
+    // ----------------------------------------------------------------------
+
+    /**
+     * SiteTool component.
+     *
+     * @since 2.1
+     */
+    @Component
+    protected SiteTool siteTool;
+
+    /**
+     * Doxia Site Renderer component.
+     */
+    @Component
+    protected Renderer siteRenderer;
+
+    /**
+     * Artifact Resolver component.
+     */
+    @Component
+    protected ArtifactResolver resolver;
+
+    /**
+     * Artifact Factory component.
+     */
+    @Component
+    protected ArtifactFactory factory;
+
+    /**
+     * Internationalization component, could support also custom bundle using {@link #customBundle}.
+     */
+    @Component
+    private I18N i18n;
+
+    // ----------------------------------------------------------------------
+    // Mojo parameters
+    // ----------------------------------------------------------------------
+
+    /**
+     * The output directory for the report. Note that this parameter is only evaluated if the goal is run directly from
+     * the command line. If the goal is run indirectly as part of a site generation, the output directory configured in
+     * the Maven Site Plugin is used instead.
+     */
+    @Parameter( property = "project.reporting.outputDirectory", required = true )
+    protected java.io.File outputDirectory;
+
+    /**
+     * The Maven Project.
+     */
+    @Component
+    protected MavenProject project;
+
+    /**
+     * Local Repository.
+     */
+    @Parameter( property = "localRepository", required = true, readonly = true )
+    protected ArtifactRepository localRepository;
+
+    /**
+     * Remote repositories used for the project.
+     *
+     * @since 2.1
+     */
+    @Parameter( property = "project.remoteArtifactRepositories" )
+    protected List<ArtifactRepository> remoteRepositories;
+
+    /**
+     * The current user system settings for use in Maven.
+     *
+     * @since 2.3
+     */
+    @Component
+    protected Settings settings;
+
+    /**
+     * Path for a custom bundle instead of using the default one. <br/>
+     * Using this field, you could change the texts in the generated reports.
+     *
+     * @since 2.3
+     */
+    @Parameter( defaultValue = "${project.basedir}/src/site/custom/downloads.properties" )
+    protected String customBundle;
+
+    // ----------------------------------------------------------------------
+    // Public methods
+    // ----------------------------------------------------------------------
+
+    @Override
+    public void execute()
+        throws MojoExecutionException
+    {
+        if ( !canGenerateReport() )
+        {
+            return;
+        }
+
+        // TODO: push to a helper? Could still be improved by taking more of the site information from the site plugin
+        Writer writer = null;
+        try
+        {
+            String filename = getOutputName() + ".html";
+
+            DecorationModel model = new DecorationModel();
+            model.setBody( new Body() );
+
+            Map<String, Object> attributes = new HashMap<String, Object>();
+            attributes.put( "outputEncoding", "UTF-8" );
+            attributes.put( "project", project );
+
+            Locale locale = Locale.getDefault();
+            Artifact defaultSkin =
+                siteTool.getDefaultSkinArtifact( localRepository, project.getRemoteArtifactRepositories() );
+
+            SiteRenderingContext siteContext =
+                siteRenderer.createContextForSkin( defaultSkin.getFile(), attributes, model, getName( locale ), locale );
+
+            RenderingContext context = new RenderingContext( outputDirectory, filename );
+
+            SiteRendererSink sink = new SiteRendererSink( context );
+
+            generate( sink, null, locale );
+
+            outputDirectory.mkdirs();
+
+            writer = new OutputStreamWriter( new FileOutputStream( new File( outputDirectory, filename ) ), "UTF-8" );
+
+            siteRenderer.generateDocument( writer, sink, siteContext );
+
+            siteRenderer.copyResources( siteContext, new File( project.getBasedir(), "src/site/resources" ),
+                                        outputDirectory );
+        }
+        catch ( RendererException e )
+        {
+            throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
+                + " report generation.", e );
+        }
+        catch ( IOException e )
+        {
+            throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
+                + " report generation.", e );
+        }
+        catch ( SiteToolException e )
+        {
+            throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
+                + " report generation.", e );
+        }
+        catch ( MavenReportException e )
+        {
+            throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
+                + " report generation.", e );
+        }
+        finally
+        {
+            IOUtil.close(writer);
+        }
+    }
+
+    @Override
+    public String getCategoryName()
+    {
+        return CATEGORY_PROJECT_INFORMATION;
+    }
+
+    // ----------------------------------------------------------------------
+    // Protected methods
+    // ----------------------------------------------------------------------
+
+    @Override
+    protected String getOutputDirectory()
+    {
+        return outputDirectory.getAbsolutePath();
+    }
+
+    @Override
+    public File getReportOutputDirectory()
+    {
+        return outputDirectory;
+    }
+
+    @Override
+    public void setReportOutputDirectory( File reportOutputDirectory )
+    {
+        this.outputDirectory = reportOutputDirectory;
+    }
+
+    @Override
+    protected MavenProject getProject()
+    {
+        return project;
+    }
+
+    protected Plugin getPlugin( String pluginId )
+    {
+        if ( ( getProject().getBuild() == null ) || ( getProject().getBuild().getPluginsAsMap() == null ) )
+        {
+            return null;
+        }
+
+        Plugin plugin = (Plugin) getProject().getBuild().getPluginsAsMap().get( pluginId );
+
+        if ( ( plugin == null ) && ( getProject().getBuild().getPluginManagement() != null ) && (
+                getProject().getBuild().getPluginManagement().getPluginsAsMap() != null ) )
+        {
+            plugin = (Plugin) getProject().getBuild().getPluginManagement().getPluginsAsMap().get( pluginId );
+        }
+
+        return plugin;
+    }
+
+    protected String getPluginParameter( String pluginId, String param )
+    {
+        Plugin plugin = getPlugin( pluginId );
+        if ( plugin != null )
+        {
+            Xpp3Dom xpp3Dom = (Xpp3Dom) plugin.getConfiguration();
+            if ( xpp3Dom != null && xpp3Dom.getChild( param ) != null
+                && StringUtils.isNotEmpty(xpp3Dom.getChild(param).getValue()) )
+            {
+                return xpp3Dom.getChild( param ).getValue();
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    protected Renderer getSiteRenderer()
+    {
+        return siteRenderer;
+    }
+
+    protected String getI18nString( Locale locale, String key )
+    {
+        return getI18N( locale ).getString( "download-links-report", locale, "report." + "download-links" + '.' + key );
+    }
+
+    protected I18N getI18N( Locale locale )
+    {
+        if ( customBundle != null )
+        {
+            File customBundleFile = new File( customBundle );
+            if ( customBundleFile.isFile() && customBundleFile.getName().endsWith( ".properties" ) )
+            {
+                if ( !i18n.getClass().isAssignableFrom( CustomI18N.class ) )
+                {
+                    // first load
+                    i18n = new CustomI18N( project, settings, customBundleFile, locale, i18n );
+                }
+                else if ( !i18n.getDefaultLanguage().equals( locale.getLanguage() ) )
+                {
+                    i18n = new CustomI18N( project, settings, customBundleFile, locale, i18n );
+                }
+            }
+        }
+
+        return i18n;
+    }
+
+    @Override
+    protected void executeReport(Locale locale) throws MavenReportException {
+        DownloadLinksRenderer r = new DownloadLinksRenderer(getSink(), getI18N(locale), locale );
+
+        r.setName(project.getName());
+
+        r.render();
+    }
+
+    public String getOutputName() {
+        return "downloads";
+    }
+
+    /** {@inheritDoc} */
+    public String getName( Locale locale )
+    {
+        return getI18nString( locale, "name" );
+    }
+
+    /** {@inheritDoc} */
+    public String getDescription( Locale locale )
+    {
+        return getI18nString( locale, "description" );
+    }
+    private static class CustomI18N
+        implements I18N
+    {
+        private final MavenProject project;
+
+        private final Settings settings;
+
+        private final String bundleName;
+
+        private final Locale locale;
+
+        private final I18N i18nOriginal;
+
+        private ResourceBundle bundle;
+
+        private final static Object[] NO_ARGS = new Object[0];
+
+        public CustomI18N( MavenProject project, Settings settings, File customBundleFile, Locale locale,
+                           I18N i18nOriginal )
+        {
+            super();
+            this.project = project;
+            this.settings = settings;
+            this.locale = locale;
+            this.i18nOriginal = i18nOriginal;
+            this.bundleName =
+                customBundleFile.getName().substring( 0, customBundleFile.getName().indexOf( ".properties" ) );
+
+            URLClassLoader classLoader = null;
+            try
+            {
+                classLoader = new URLClassLoader( new URL[] { customBundleFile.getParentFile().toURI().toURL() } );
+            }
+            catch ( MalformedURLException e )
+            {
+            }
+
+            this.bundle = ResourceBundle.getBundle( this.bundleName, locale, classLoader );
+            if ( !this.bundle.getLocale().getLanguage().equals( locale.getLanguage() ) )
+            {
+                this.bundle = ResourceBundle.getBundle( this.bundleName, Locale.getDefault(), classLoader );
+            }
+        }
+
+        /** {@inheritDoc} */
+        public String getDefaultLanguage()
+        {
+            return locale.getLanguage();
+        }
+
+        /** {@inheritDoc} */
+        public String getDefaultCountry()
+        {
+            return locale.getCountry();
+        }
+
+        /** {@inheritDoc} */
+        public String getDefaultBundleName()
+        {
+            return bundleName;
+        }
+
+        /** {@inheritDoc} */
+        public String[] getBundleNames()
+        {
+            return new String[] { bundleName };
+        }
+
+        /** {@inheritDoc} */
+        public ResourceBundle getBundle()
+        {
+            return bundle;
+        }
+
+        /** {@inheritDoc} */
+        public ResourceBundle getBundle( String bundleName )
+        {
+            return bundle;
+        }
+
+        /** {@inheritDoc} */
+        public ResourceBundle getBundle( String bundleName, String languageHeader )
+        {
+            return bundle;
+        }
+
+        /** {@inheritDoc} */
+        public ResourceBundle getBundle( String bundleName, Locale locale )
+        {
+            return bundle;
+        }
+
+        /** {@inheritDoc} */
+        public Locale getLocale( String languageHeader )
+        {
+            return new Locale( languageHeader );
+        }
+
+        /** {@inheritDoc} */
+        public String getString( String key )
+        {
+            return getString( bundleName, locale, key );
+        }
+
+        /** {@inheritDoc} */
+        public String getString( String key, Locale locale )
+        {
+            return getString( bundleName, locale, key );
+        }
+
+        /** {@inheritDoc} */
+        public String getString( String bundleName, Locale locale, String key )
+        {
+            String value;
+
+            if ( locale == null )
+            {
+                locale = getLocale( null );
+            }
+
+            ResourceBundle rb = getBundle( bundleName, locale );
+            value = getStringOrNull( rb, key );
+
+            if ( value == null )
+            {
+                // try to load default
+                value = i18nOriginal.getString( bundleName, locale, key );
+            }
+
+            if ( value.indexOf( "${" ) < 0 )
+            {
+                return value;
+            }
+
+            final RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
+            try
+            {
+                interpolator.addValueSource( new EnvarBasedValueSource() );
+            }
+            catch ( final IOException e )
+            {
+            }
+
+            interpolator.addValueSource( new PropertiesBasedValueSource( System.getProperties() ) );
+            interpolator.addValueSource( new PropertiesBasedValueSource( project.getProperties() ) );
+            interpolator.addValueSource( new PrefixedObjectValueSource( "project", project ) );
+            interpolator.addValueSource( new PrefixedObjectValueSource( "pom", project ) );
+            interpolator.addValueSource( new PrefixedObjectValueSource( "settings", settings ) );
+
+            try
+            {
+                value = interpolator.interpolate( value );
+            }
+            catch ( final InterpolationException e )
+            {
+            }
+
+            return value;
+        }
+
+        /** {@inheritDoc} */
+        public String format( String key, Object arg1 )
+        {
+            return format( bundleName, locale, key, new Object[] { arg1 } );
+        }
+
+        /** {@inheritDoc} */
+        public String format( String key, Object arg1, Object arg2 )
+        {
+            return format( bundleName, locale, key, new Object[] { arg1, arg2 } );
+        }
+
+        /** {@inheritDoc} */
+        public String format( String bundleName, Locale locale, String key, Object arg1 )
+        {
+            return format( bundleName, locale, key, new Object[] { arg1 } );
+        }
+
+        /** {@inheritDoc} */
+        public String format( String bundleName, Locale locale, String key, Object arg1, Object arg2 )
+        {
+            return format( bundleName, locale, key, new Object[] { arg1, arg2 } );
+        }
+
+        /** {@inheritDoc} */
+        public String format( String bundleName, Locale locale, String key, Object[] args )
+        {
+            if ( locale == null )
+            {
+                locale = getLocale( null );
+            }
+
+            String value = getString( bundleName, locale, key );
+            if ( args == null )
+            {
+                args = NO_ARGS;
+            }
+
+            MessageFormat messageFormat = new MessageFormat( "" );
+            messageFormat.setLocale( locale );
+            messageFormat.applyPattern( value );
+
+            return messageFormat.format( args );
+        }
+
+        private String getStringOrNull( ResourceBundle rb, String key )
+        {
+            if ( rb != null )
+            {
+                try
+                {
+                    return rb.getString( key );
+                }
+                catch ( MissingResourceException ignored )
+                {
+                    // intentional
+                }
+            }
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/plugins/maven-downloads-plugin/src/main/resources/download-links-report.properties b/plugins/maven-downloads-plugin/src/main/resources/download-links-report.properties
new file mode 100644
index 0000000..c09e80a
--- /dev/null
+++ b/plugins/maven-downloads-plugin/src/main/resources/download-links-report.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+report.download-links.title = Download {0}
+report.download-links.name = Download
\ No newline at end of file