Maven: multi-project and dashboard issues

Some Maven multiproject experiences will be described in this post. Special attention is paid to the dashboard plug-in.

Maven: multi-project and dashboard issues

Generic Maven project structure

Let us first reiterate the canonical structure of a Maven
(sub)project:

Canonical Maven Project Lay-out

This project structure will henceforth implicitly be assumed for
all subprojects constituting an overall superproject.

A canonical multiproject lay-out

Although most if not all plug-ins work flawlessly with the
multiproject structure as outlined below, it is not a universally
applicable lay-out. For example, this lay-out does’t integrate
well with the Eclipse or WSAD IDEs.

However, for a proper understanding, let us first shortly
(re)introduce the canonical multiproject structure, that is
(sometimes implicitly) assumed in most Maven and
Maven plug-in related documentation.

Canonical multiproject structure

The lay-out of a canonical multiproject project is as follows
(where we collapsed the subproject tree structure as was listed
above):

Canonical multi-project

Invoking

$ maven multiproject:site

a target directory is generated containing the maven generated
site.

The advantage is that set aside some customizations and/or
optimizations, a maven.xml is not needed, hence
your Maven (and Jelly) related knowledge can be restricted
to the bare minimum. Moreover, you project structure lay-out
will be Maven generic!

Below we will focus a bit more on the precise contents of
the project.xml and the project.properties,
in order to be able to point out the differences with our
more generalized case that will be discussed thereafter.

Contents project.xml

An example project.xml has been listed below.
We made use of the possibility to inherit (extend) from a top-level
project.xml file, located in one of the subprojects.
This is especially usefull when dealing with a project consisting
of a lot of subprojects and is almost always employed.

This project.xml is responsible for the main (overview)
project site. The dashboard and multiproject plug-ins are configured
here, the former generating a summary of project metrics
(which metrics are to be included can be configured, see
the project.properties below),
the latter analyzing the version convergency of the
dependencies of all subprojects on (external) artifacts,
such as JAR and ZIP files.

Of course, other plug-ins, such as the mutlichanges plug-in can
and may be configured here as well, but we will refrain from
doing so for the sake of clarity.

A template project.xml is shown below:

<?xml version="1.0" encoding="UTF-8"?>
<project>
  <extend>sub-project-m/top-level-project.xml</extend>
  <pomVersion>3</pomVersion>
  <id>site</id>
  <name>Overall Site</name>

  <reports>
    <report>maven-dashboard-plugin</report>
    <report>maven-multiproject-plugin</report>
  </reports>
</project>

Contents project.properties

Below we have only listed those properties
from the project.properties file that are relevant
to the multi-project plug-in (and reactor) and the
dashboard plug-in and associated overall report generation:

#
# Multiproject
#
maven.mutliproject.basedir=.
maven.multiproject.includes=sub-project-*/project.xml
maven.multiproject.excludes=sub-project-DontBuildMe
#
# Dashboard properties
#
# Tells the dashboard plugin not to run its own reactor.
# We’ll use the one run by the multiproject.
maven.dashboard.runreactor = false
#
# Tells the multiproject plugin to execute the dashboard:report-single
# goal on each project when you run it through multiproject:site.
maven.multiproject.site.goals=site, dashboard:report-single
#
maven.dashboard.aggregators = csfiles,cswarnings,cserrors,junittests,junitfailures,juniterrors,junitpassrate,pmdfiles,pmdviolations,simiantdl,jcoveragelipc,jcoverageloc

The multiproject related properties don’t need to be explained,
except maybe for the maven.multiproject.excludes: this
property is optional and can be used for those subprojects that must
(temporarily) be exempted from the build process.

Dashborad report generation

It turns out that the dashboard plug-in seamlessly integrates
with this canonical multiproject structure. An integrated
summary will be available under the project report section.
An example of such a summary is listed on the Dashboard plug-in page,
http://maven.apache.org/reference/plugins/dashboard/.

Of course, for those subprojects that have dashboard reporting
enabled in the report section of their POM (project.xml),
a dashboard report for the subproject itself is included in the
project reports section.

Multiproject with root subproject

The need for a more generalized lay-out has been motivated at the
beginning of this document. In practice, this "root"
subproject may be named differently, such as main project, site
project or deployment project.

Multiproject with root dir structure

The lay-out of a canonical multiproject project that contains
a root project on the same level as the subprojects is depicted
below:

Multi-project with root dir on same level as subprojects

The differences with the canonical case are immediately obvious:

  • The project.xml and
    project.properties have moved to the root
    subproject
  • A maven.xml is mandatory now
  • The root project is on the same level as the subprojects
  • Site pages reside in the site dir.

All these small differences imply many small intricate mutations
as compared to the canonical case. These changes will be
elaborated on below.

Contents maven.xml

Let us first focus on the maven.xml file.

<goal name="myProject:site"
      description="Build site for the master project and all subprojects">

  <echo message="+-----------------------------------------------+"/>
  <echo message="| First building sites of separate sub projects |"/>
  <echo message="+-----------------------------------------------+"/>

  <maven:reactor
             basedir="${basedir}/.."
             includes="${maven.multiproject.includes}"
             excludes="${maven.multiproject.excludes}"
             goals="dashboard:report-single, dashboard:report, xdoc"
             banner="Build site for subproject "
             postProcessing="true"
             ignoreFailures="true"/>

  <echo message="+----------------------------------+"/>
  <echo message="| Performing dashboard aggragation |"/>
  <echo message="+----------------------------------+"/>

  <attainGoal name="dashboard:aggregate"/>
  <attainGoal name="myProject:mainSite"/>
</goal>

<goal name="myProject:mainSite"
      description="Build main (root/overview) pages">

  <echo message="+-------------------------------------+"/>
  <echo message="| Building main (root/overview) pages |"/>
  <echo message="+-------------------------------------+"/>

  <maven:reactor
             basedir="${basedir}"
             includes="site/project.xml"
             goals="allSite"
             banner="Build site for myProject using "
             postProcessing="false"
             ignoreFailures="true"/>

  <echo message="+-----------------------------------+"/>
  <echo message="| Copying all to target destination |"/>
  <echo message="+-----------------------------------+"/>

  <copy todir="${basedir}/target">
      <fileset dir="${basedir}/site/target">
          <include name="**/*" />
      </fileset>
  </copy>
</goal>

The maven.xml is composed as follows:

  1. The myProject:site builds the whole
    site, i.e. the main site and of all subprojects.
  2. It starts off by invocation of the reactor (standard)
    for the site generation of all subprojects.
  3. Note the goals that are invoked for each separate subproject!
    This is essential to get the dashboard reports
    right.
  4. Thereafter, an aggregate dashboard report is generated.
  5. The overall site is generated. This will require a
    section by itself. Note how the generation of the
    main site is "handed over" to the
    allSite goal of the maven.xml
    in the site directory.
  6. All files are copied to the target
    directory in the root level project directory.
    Needless to say, any destination will do, as well as a
    deploy to web server, of course.

Contents project.properties

Below we have only listed those properties
from the project.properties file that are relevant
to the project lay-out (and dashboard report generation):

#
# Multiproject
#
maven.multiproject.basedir=..
maven.multiproject.includes=sub-project-*/project.xml
maven.multiproject.excludes=sub-project-root/project.xml
#
# Dashboard properties
#
# Tells the dashboard plugin not to run its own reactor.
# We’ll use the one run by the multiproject.
maven.dashboard.runreactor = false
#
maven.dashboard.aggregators = csfiles,cswarnings,cserrors,junittests,junitfailures,juniterrors,junitpassrate,pmdfiles,pmdviolations,simiantdl,jcoveragelipc,jcoverageloc,javancssncsstotal,javancssjavadocstotal

The properties and their values should be obvious from inspection.
Notice the dashboard relevant properties especially: not more, not
less is needed.

Finally, for dashboard to work properly, you must
incorporate the following lines in the
project.properties of each of the
subprojects:

maven.dashboard.rungoals=true
maven.dashboard.includes=project.xml

Overall site generation

The site pages, as well as the overview of all the subprojects
and the aggregate dashboard report are generated based on
the material residing in the site
directory. Rationale: since we deviated from the canonical
project lay-out, the multiproject nor the dashboard plug-ins
"know" how to handle this situation, and special
adaptations have been incorporated to cope with this new situation.

Let us first focus on the overview of all subprojects.
This page is generated from the projects-overview.xml
file in the xdocs-templates directory:

<?xml version="1.0"?>
<document>
  <properties>
    <title>Overview of projects</title>
  </properties>
  <body>
    <section name="Overview of projects">
      <table>
      #foreach ($reactorProject in $reactorProjects)
        <tr>
          <td><b><a href="${reactorProject.file.parentFile.name}/index.html">${reactorProject.artifactId}</a></b></td>
          <td>${reactorProject.shortDescription}</td>
        </tr>
      #end
      </table>
    </section>
  </body>
</document>

Using velocity, this template is transformed into a
projects-overview.xml in the xdocs
directory, which in turn is transformed into an HTML document
by the xdoc plug-in.

This transformation process is controlled by the
maven.xml in the site directory:

<project
  default="allSite"
  xmlns:j="jelly:core"
  xmlns:maven="jelly:maven"
  xmlns:x="jelly:xml"
  xmlns:u="jelly:util"
  xmlns:a="jelly:ant"
  xmlns:velocity="jelly:org.apache.commons.jelly.tags.velocity.VelocityTagLibrary">

  <goal name="allSite">
    <attainGoal name="subSite"/>
    <attainGoal name="dashboard:xdoc"/>
    <attainGoal name="site"/>
  </goal>

  <goal name="subSite"
        description="Build the overview for all subprojects"
        >
    <maven:reactor
               basedir="${basedir}/../.."
               includes="${maven.multiproject.includes}"
               excludes="${maven.multiproject.excludes}"
               goals="pom"
               sort="true"
               banner="Building subprojects overview"
               postProcessing="true"
               ignoreFailures="false"/>

    <attainGoal name="dashboard:aggregate"/>

    <!--
     |
     | At this point because 'postProcessing' was turned on we have all
     | the processed POMs available to us in the ${reactorProjects} variable.
     |
     | The overall project page can now be generated.
     |
     -->

    <fileScanner var="templates">
      <fileset dir="${basedir}/xdocs-templates">
        <include name="*.xml"/>
      </fileset>
    </fileScanner>
    <j:set var="templatesX" value="${templates}X"/>
    <j:if test="${!templatesX.equals('X')}">
      <j:forEach var="template" items="${templates.iterator()}">

        <j:set var="templateName" value="${template.name}"/>
        <echo>Processing ${templateName} template ...</echo>

        <velocity:merge
          name="${basedir}/xdocs/${templateName}"
          basedir="${basedir}/xdocs-templates"
          template="${templateName}"/>

      </j:forEach>
    </j:if>

    <!--
     |
     | The sites of the various subprojects are now copied over to the main
     | site.
     |

    -->
    <j:forEach var="reactorProject" items="${reactorProjects}">
        <echo>Copying target dirs from project ${reactorProject.artifactId} to ${basedir}</echo>
        <copy todir="${basedir}/target/docs/${reactorProject.artifactId}">
           <fileset dir="${basedir}/../../${reactorProject.artifactId}/target/docs">
               <include name="**/*" />
           </fileset>
        </copy>
     </j:forEach>
  </goal>
</project>

Note how

  • The reactor merely builds the POM, in order to have
    all subprojects available, so that we can iterate over
    them. With this iteration, the projects overview page
    and the dashboard report can be constructed.
  • The subSite goal generates the overview page
  • Where and how the dashboard aggreation takes place
  • The dashboard aggregate data (in
    dashboard-data.xml) is transformed into
    a report (in dashboard-report.xml), which
    will eventually be transformed into a
    dashboard-report.html.
  • How the site material of the subprojects is copied over
    to the overall destination.

In order for the generation of the overview page to
work properly, the following dependencies have to be
incorporated in the project.xml in the
site directory:

<?xml version="1.0" encoding="UTF-8"?>
<project>
  <extend>../bibtexml-top-level-project.xml</extend>
  <pomVersion>3</pomVersion>
  <id>site</id>
  <name>BibTeXML Site</name>
  <dependencies>
    <dependency>
      <id>commons-jelly+tags-velocity</id>
      <version>SNAPSHOT</version>
    </dependency>

    <dependency>
      <id>velocity</id>
      <version>1.3</version>
    </dependency>
  </dependencies>

  <reports>
    <report>maven-multiproject-plugin</report>
    <!--report>maven-multichanges-plugin</report-->
    <!--report>maven-tasklist-plugin</report-->
  </reports>
</project>

Moreover, if you want the multiproject convergency report,
you have to enable it here. Do not enable
the dashboard report here: it won’t work, and what’s even
worse, it will overwrite our so carefully aggregated
report with an empty one!

15 Comments

  1. Pingback: AdSense Money Maker April 17, 2007
  2. emerson September 28, 2005
  3. Zeger Hendrikse July 20, 2005
  4. Zeger Hendrikse July 12, 2005
  5. Khiet Tran July 7, 2005
  6. Zeger Hendrikse May 30, 2005
  7. Rupert Smith May 30, 2005
  8. Rupert Smith May 27, 2005
  9. Rupert Smith May 27, 2005
  10. Rupert Smith May 27, 2005
  11. Rupert Smith May 27, 2005
  12. カジノ April 17, 2005
  13. Lucas April 5, 2005
  14. Zeger Hendrikse April 5, 2005
  15. Zeger Hendrikse April 5, 2005
  16. Stephan H. Wissel April 5, 2005