Sonatype

Previous: Chapter 9

No one wants advice, only collaboration. -- John Steinbeck

This chapter follows an example project. You may wish to download the site-generation examples and follow along.

Introduction

Most software projects today are developed by more than one author. If a project is successful, it will have more than one user. These may seem like trivial observations, but they both point to a single, critical need for every software project, that enables its development and user communities to thrive: a project website.

Project-oriented websites often contain all sorts of material, from FAQs and new-user guides to design documents, code reports, issue tracking, and more. However, keeping this much information up-to-date and relevant to the community can be a never-ending job. Perhaps this is why the websites of so many open-source projects fall hopelessly out of date.

Fortunately, Maven provides a much easier approach for maintaining your project's web content. Using Maven, you can:

Best of all, Maven can do all of this for you -- and then deploy the generated website -- with a single command. So, you've heard enough; where do we start? The same place we always start in the programming world: with a simple "Hello, World" example.

Hello World: Building A Simple Project Website

Before we can start building a project website, we need a project. For this, we turn to the trusty archetype plugin:

$ mvn archetype:create -DgroupId=book.sitegen -DartifactId=hello-world

This should give us a nice, basic project structure to work with, including a sample Java class and its accompanying JUnit test class. It also gives us a very bare-bones POM, which will need a lot of TLC if we're going to give our community a useful website.

For starters, let's see what sort of website Maven will generate from the plain-vanilla archetype result:

$ cd hello-world
$ mvn site:run

Once this build completes, fire up your browser and direct it to http://localhost:8080. You should see something similar to the following:

This is our basic hello-world website

Publishing Project Documentation

The default project website is woefully inadequate as a source of documentation for the community. Users - and developers - look to your project's website for a wide range of information and resources:

Not to worry; creating such project documentation is a fairly straightforward process when you use Maven. It consists of three basic steps:

  1. Define your website's menu by writing a site descriptor.
  2. Write your project documentation, using one or more of the document formats supported by Maven.
  3. Deploy the website.

For the purposes of the following examples, we'll define a relatively simple site structure. We'll start with the assumption that the main site content should be user documentation, with developer-oriented documentation relegated to a sub-section called Developer Resources.

Though it's possible to suppress the reports we generated in the examples above, we will leave them in at the default location within this website. In more advanced use cases, you may choose to suppress the project reports when you have a project that is dedicated solely to producing a website with user-oriented content; project reports then become nested in the generated, developer-oriented project website.

For now, let's keep things simple.

Writing a Basic Site Descriptor

First, let's get a rough outline going for the site. Start by pasting the following XML into a file called site.xml in the src/site directory of our hello-world project:

<project name="Hello World">
  <body>
    <menu ref="reports"/>
  </body>
</project>

At this point, if you regenerate the project website (you may have to clean first - always a good tip in maven when experimenting)

mvn clean site

and refresh your browser, you should see exactly the same thing as before, with the exception of the "Hello World" link in the navigation bar at the top-right of the page. From here, we can add a project banner that links back to the main page, along with a secondary logo:

<project name="Hello World">

  <bannerLeft>
    <name>Hello, World</name>
    <src>images/banner-left.png</src>
    <href>http://dev.example.com/sites/hello-world</href>
  </bannerLeft>
  
  <bannerRight>
    <src>images/banner-right.png</src>
  </bannerRight>
  ...
</project>

This time when you regenerate and refresh, you'll immediately notice that the page headers are all gone! This is because we haven't yet put the logo images in the site directory structure, so they aren't included in the generated website. To fix this, copy the banner-left.png and banner-right.png from the examples/hello-world-resources directory into the src/site/resources/images directory of the hello-world project, and regenerate once more. This time, the banner images should appear normally.

GOTCHA! Notice that the image src tags in the site.xml use the images directory, not the resources/images directory. This is because Maven uses the resources for website resources (including pre-existing HTML files) that don't need any processing. When the website is generated, Maven simply copies the contents of this directory, if it exists, directly into the generated website. At that time, what was in resources/images winds up in images in the new website.

Now that we've mastered the large-scale look-and-feel given by the site.xml, we need to create a custom navigational menu to separate user content from developer content. Typically, user documentation will contain things like features, screenshots, installation and configuration guides, and frequently asked questions. As an example of user content, then, let's create a few menu items reflecting this sort of content:

<project name="Hello World">
  ...
  <body>

    <menu name="Main Menu">
      <item name="Introduction" href="index.html"/>
      <item name="News" href="news.html"/>
      <item name="Features" href="features.html"/>
      <item name="Installation" href="installation.html"/>
      <item name="Configuration" href="configuration.html"/>
      <item name="FAQ" href="faq.html"/>
    </menu>
    ...
  </body>
</project>

Regenerate, and this time two things should pop out at you about the new Main Menu section. First, both the Introduction and Project Information -> About menu items are highlighted. This happens because the default index.html file for a Maven project website is the About report from the project-info reports plugin. Don't worry about this; we'll fix it soon.

The second thing you'll probably notice about the website is that none of the new menu items - with the exception of the aforementioned Introduction link - work! Remember, we haven't yet created any documentation beyond the default reports; providing content for these (and other) links comes next, after we complete the Developer Resources menu.

So, without further ado, let's create a menu for developer content. This sort of content usually includes architectural diagrams, integration guides, and similar items. Let's create a small developers' menu that reflects some of these elements:

<project name="Hello World">
  ...
  <body>
    ...
    <menu name="Main Menu">
      ...
      <item name="Developer Resources" href="/developer/index.html" collapse="true">
        <item name="System Architecture" href="/developer/architecture.html"/>
        <item name="Embedder's Guide" href="/developer/embedding.html"/>
      </item>
    </menu>
    ...
  </body>
</project>

TIP: The collapse="true" attribute in the Developer Resources menu item is a hint to Maven that this is a sub-menu which should be collapsed into a single menu item until it is clicked. Once clicked, its contents should be displayed in a sub-list of the menu navigation section.

Regenerate again, and you should see the new menu items (with broken links) under a new Developer Resources sub-menu. Now, we're ready to write the content that will fix these broken links. But first, let's take a brief look at some of the documentation features supported by Maven.

Writing Your Project Documentation

Project documentation often exists (or is created) in multiple formats, reflecting the different processes or tools used in the different stages of the project's development lifecycle. Blending these different formats together into a seamless, well-tailored website can be a daunting task, often involving exporting the documents to HTML using some native tool or other, and massaging the HTML from these disparate exports into a common look and feel. This can be a very hands-on process, consuming hours everytime an update occurs in the source documents.

To address this diversity, Maven uses a documentation-processing engine called Doxia, which reads multiple source formats into a common document model, applies any embedded Doxia macros, then renders documents from that model into an output format, such as XHTML. Using Maven (with Doxia) allows you to delegate the process of creating or updating your project website to an automated process, which can then be run at any time using a simple task scheduler such as cron.

Documentation Formats

Doxia's support for multiple formats means you have the freedom to mix-and-match source formats in order to use the best format for each document in your site. Currently, Doxia has support for Almost Plain Text (APT), XDoc (a Maven 1.x documentation format), XHTML, and FML (useful for FAQ documents) formats. It also has experimental support for Twiki and Confluence wiki syntaxes. Beyond these - though the topic is beyond the scope of this chapter - Doxia is relatively simple to extend. Supporting new source-document formats is just a matter of parsing the source document and generating a series of Doxia events.

For more information about some of the different source formats supported by Doxia, see the following:

  1. APT Reference: http://maven.apache.org/doxia/format.html
  2. XDoc Reference: http://jakarta.apache.org/site/jakarta-site2.html

Directory Structure for Website Sources

In order to make a more clean separation of the different document-source formats, Maven enforces a fairly strict one-format-per-subdirectory rule on the src/site directory structure. The only exception to this rule are those source files which are meant to be passed directly through as-is; these reside in the src/site/resources directory (a good example of this is the PNG files used in the site banners for the hello-world project, discussed previously). Aside from this special directory, in general source documents should reside in a structure like this:

src/site/<lower-case-file-extension>

As an example, consider the following directory structure which contains a variety of APT, XHTML, PNG, XDoc, and FML files:

hello-world
+- src/
   +- site/
      +- apt/
      |  +- index.apt
      |  +- about.apt
      |  |
      |  +- developer/
      |     +- embedding.apt
      |
      +- fml/
      |  +- faq.fml
      |
      +- resources/
      |  +- images/
      |  |  +- banner-left.png
      |  |  +- banner-right.png
      |  |
      |  +- architecture.html
      |  +- jira-roadmap-export-2007-03-26.html
      |
      +- xdoc/
      |  +- xml-example.xml
      |
      +- site.xml

GOTCHA! The XDoc files in the above example actually have a file extension of .xml. This represents a deviation from the aforementioned rule of directory structures; the main reason for this deviation is that a directory called xml is not descriptive enough to make it clear that the documents within are meant to adhere to the XDoc format.

One other thing to notice about the directory structure above is the developer directories under apt and resources. These, along with the images directory, will be mapped into the root of the resulting website. You can think of the format directories as being a root-directory fragment; when the website is generated, their contents are rendered to XHTML and merged into a single directory structure, with subdirectories intact. The only change is in the file extension: from apt (or xml, etc.) to html.

Macros

In addition to its advanced document rendering features, Doxia also provides a macro engine that allows each input format to trigger injection of dynamic content. An excellent example of this is the snippet macro, which allows a document to pull a code snipped out of a source file that's available via HTTP (using a web-enabled version control system, for instance). Using this macro, a small fragment of APT can be rendered into XHTML:

Usage of the snippet macro in APT syntax:

%{snippet|id=modello-model|url=http://svn.apache.org/repos/asf/maven/archetype/trunk/maven-archetype/maven-archetype-model/src/main/mdo/archetype.mdo}

Output of the snippet macro in XHTML:

<div class="source"><pre>

<model>
  <id>archetype</id>
  <name>Archetype</name>
  <description><![CDATA[Maven's model for the archetype descriptor.]]></description>
  <defaults>
    <default>
      <key>package</key>
      <value>org.apache.maven.archetype.model</value>
    </default>
  </defaults>
  <classes>
    <class rootElement="true" xml.tagName="archetype">
      <name>ArchetypeModel</name>
      <description>Describes the assembly layout and packaging.</description>
      <version>1.0.0</version>
      <fields>
        <field>
          <name>id</name>
          <version>1.0.0</version>
          <required>true</required>
          <type>String</type>
        </field>
        ...
      </fields>
    </class>
  </classes>
</model>

</pre></div>

GOTCHA! Doxia macros MUST NOT be indented in APT source documents. Doing so will result in the APT parser skipping the macro altogether.

For more information about defining snippets in your code for reference by the snippet macro, see the Guide to the Snippet Macro on the Maven website, at http://maven.apache.org/guides/mini/guide-snippet-macro.html.

Currently, the only useful macro supported out-of-the-box by Doxia is the snippet macro. Again, though it's beyond the scope of this chapter, building your own Doxia macros is a relatively simple process. See the Guide to Doxia Macros, at http://maven.apache.org/doxia/guide-doxia-macros.html for more information.

Examples

Taking what we now know about site documentation and Maven, we can construct a small end-to-end example containing some of the content we hinted at previously in our site descriptor src/site/apt/index.apt, Introduction:

  ---
  Introduction
  ---
  Andrea Penn
  ---
  26-Mar-2007
  ---
 
  Welcome!
 
  Hello-World is a small sample project built using Maven. 
  It even uses Maven to generate this website.
 
  Here are some useful links to get you started:
 
   * {{{news.html}News}}
 
   * {{{features.html}Features}}
 
   * {{{faq.html}FAQ}}
 
   []

src/site/apt/developer/index.apt, Developer Resources:

 ---
 Developer Resources
 ---
 Andrea Penn
 ---
 04-Apr-2007
 ---
 
 This section is meant to guide hello-world developers and integrators, 
 by providing information on the development process, system architecture,
 and APIs available.

 Quick Links

 * {{{architecture.html}System Architecture}}

 * {{{embedding.html}Embedding Guide}}

 []

src/site/fml/faq.fml, FAQ:

<?xml version="1.0" encoding="UTF-8"?>
<faqs title="Frequently Asked Questions">
  <part id="General">
    <faq id="dead-doo-dad">
      <question>My doo-dad is dead. How can I regenerate it?</question>
      <answer>
        <p>
          Doo-dad generation happens at system boot. Therefore, the simplest
          answer is to restart the hello-world-doodad service.
        </p>
        <p>
          If your doodad service is widget-enabled, you can also regenerate
          dead doo-dads using the /widgets/reincarnate.doodad address and the
          following message data:
        </p>
        <source>
          <reincarnation>
            <id>1</id>
            <password>ThisShouldBeEncrypted</password>
          </reincarnation>
        </source>
      </answer>
    </faq>
    <faq id="what-is-a-doo-dad">
      <question>What the h@!! is a doo-dad anyway?</question>
      <answer>
        <p>
          Doo-dads are components of the hello-world system used to obfuscate
          the misdirection server. It is critical to system health that the
          doo-dad pool have an appropriate threshold and that it be culled
          regularly.
        </p>
      </answer>
    </faq>
  </part>
</faqs>

NOTE: The content of the answer section is not pure XHTML. In fact, this section is formatted using XDoc syntax, which is nearly XHTML, with some added goodies like the source element.

This time, if you regenerate and refresh, you should see a new front page to the project website, one that reflects the content of the new APT document we just wrote. Likewise, if you click on the FAQ link (either in the menu at left, or in the main page), you should see the two entries we just added to the faq.fml page, nicely rendered into XHTML.

Notice that the newly generated website doesn't contain two menu references for the main page. This happens when we create a file called index.apt in the root apt format directory. Since there is concrete main page for the site, Maven assumes that the About report is no longer needed, so it's no longer generated. Also, this doesn't have to be an apt file; any file in any of the format directories (or the resources directory) called index with the appropriate extension for the format will suppress the About report and take its place on the main page.

Next, turn your attention to the Developer Resources link. If you click this link, you should see that our landing page for developers is displayed, and that the Developer Resources menu item is expanded to show the contents underneath it. This expansion is a result of the initial site-descriptor hint of collapse="true" on the Developer Resources menu item.

Finally, notice that the source files are all named with an extension that is appropriate to identify it as a file of a particular format (with the exception of XDoc sources). It's critical to remember that, regardless of the source file's extension, all references to these pages inside the content must reference the rendered page, not the source page. That is, any link to the index page must reference index.html, not index.apt. You can see this rule at work in the site descriptor, above.

This should get you started writing content for the hello-world project. Filling in the remainder of the links shown in the site menu is left as an exercise for the reader.

Deploying Your Project Website

NOTE: It should go without saying, but we'll say it anyway: you'll have to supply your own DAV server and deployment URL for this example to work properly. The following is provided for illustrative purposes.

After you iterate on the content of your project's documentation source a few times, and you feel it's ready to go live, you still need a mechanism for publishing out to your webserver. To address this, Maven's site plugin has a deployment feature that can handle several network protocols, including FTP, SCP, and DAV. For the purposes of our example, we'll stick to DAV. To use this deployment mechanism, you must first configure the site entry of the distributionManagement section in the POM, like this:

<project>
  ...
  <distributionManagement>
    <site>
      <id>hello-world.website</id>

      <!-- Set to the deployment URL on your own DAV server. -->
      <url>dav:https://dav.dev.example.com/sites/hello-world</url>
    </site>
  </distributionManagement>
  ...
</project>

NOTE: In much the same way as the SCM URLs above (in the Source Repository report discussion), the URL in this snippet has two protocols; the first one is trimmed from the rest of the URL - which is used as the connection URL for the deployment - and tells Maven what sort of connection to use (in our case, DAV).

Once we've added the distributionManagement section to our hello-world POM, we can try deploying the site:

$ mvn clean site-deploy

Configuring Server Options for Deployment

In some cases, a simple deployment like the one above will not work because you haven't supplied the correct username or password. At other times, such a deployment may cause problems for others because your deployment used the wrong file or directory mode. Whether you need to configure a username or a directory mode, the file you should edit is the settings.xml, usually located in the .m2 subdirectory within your home directory. All configuration that pertains to our site deployment will happen inside the servers/server section with an id of 'hello-world.website'.

Configuring Server Authentication

To configure a username/password combination for use during the site deployment, we'll include the following in $HOME/.m2/settings.xml:

<settings>
  ...
  <servers>
    <server>
      <id>hello-world.website</id>
      <username>jsmith</username>
      <password>BadPassword</password>
    </server>
    ...
  </servers>
  ...
</settings>

TIP: In the event you're using SCP for deployment, you may wish to use public-key authentication. To do this, specify the publicKey and passphrase elements, instead of the password element. You may still want to configure the username element, depending on your server's configuration.

Configuring File and Directory Modes

To configure specific file and directory modes for use during the site deployment (modes control file and directory access among file owners, their groups, and the rest of the world), we'll include the following in $HOME/.m2/settings.xml:

<settings>
  ...
  <servers>
    ...
    <server>
      <id>hello-world.website</id>
      ...
      <directoryPermissions>0775</directoryPermissions>
      <filePermissions>0664</filePermissions>
    </server>
  </servers>
  ...
</settings>

The above settings will make any directories readable, writable, and listable by either the owner or members of the owner's primary group; the rest of the world will only have access to read and list the directory. Similarly, the owner or members of the owner's primary group will have access to read and write any files, with the rest of the world restricted to read-only access.

Tuning Your Project Website

Sometimes, the default look and feel of a Maven project website isn't enough. You may wish to customize your project's website beyond simply adding content, navigational elements, and custom logos. Maven offers several mechanisms for customizing your website that offer successively deeper access to content decoration and website structure. For small, per-project tweaks, providing a custom site.css is often enough. However, if you want your customizations to be reusable across multiple projects, or if your customizations involve changing the XHTML that Maven generates, you should consider creating your own Maven website skin.

In order to demonstrate the types of customization available in both scenarios, we'll explore the site.css and experiment with building our own site skin below.

Create a Custom site.css

The simplest way to customize a Maven-generated website a single project is to provide a site.css file. Just like any images or XHTML content you provide for the website, the site.css file goes in the src/site/resources directory. More precisely, Maven expects this file to be in the src/site/resources/css subdirectory. This file allows you to override any theme-specific or global website style for your own project website only.

For example, if we decided that to change our hello-world website so that our menu headings stand out a little more, we might try the following:

src/site/resources/css/site.css

#navcolumn h5 {
  font-size: smaller;
  border: 1px solid #aaaaaa;
  background-color: #bbb;
  margin-top: 7px;
  margin-bottom: 2px;
  padding-top: 2px;
  padding-left: 2px;
  color: #000;
}

When you regenerate the website, you should notice that the menu headers are framed by a gray background, and separated from the rest of the menu content a little more. Using this file, any structure in the Maven-generated website can be decorated with custom CSS. The only drawback is that these styles are specific to the current project, which could mean that the project's look and feel doesn't integrate smoothly into a larger web presence. To make your custom site styles reusable, you'll have to build a custom skin.

TIP: Maven doesn't publish an annotated reference of the site structure or CSS styles used by the the site plugin. Although this site structure can vary if you're using a custom page template (we'll talk more about these below), not even the default page structure is documented. This can make discovering which id's and classes to override in your website a serious chore - unless you use a special CSS editing tool. I highly recommend the Web Developer and Firebug extensions for FireFox for this sort of work. Using these tools, you can view attributes such as HTML element hierarchy and CSS attributes that are in effect on any element from a loaded web page. In addition, you can even experiment with new CSS and see your changes reflected on the page instantaneously. Best of all, these are open-source tools! For more information, see the Resources section at the end of this chapter.

Create a Custom Site Template

At times, it's not just a simple matter of wanting a slightly different look ant feel for your project website. At times, you may want to add entire new features to the structure of the XHTML that is rendered. One such example is Javascript-enabled menus. Fortunately, Maven allows you to customize this sort of page-rendering through the incorporation of a custom page template. To illustrate, let's create a custom page template to make the hello-world website's menus come alive with the power of Javascript.

The maven-site-plugin uses a rendering engine called Doxia, which in turn uses a Velocity template to render the XHTML for each page. To change the page structure that is rendered by default, we can configure the site plugin in our POM to use a custom page template. We'll start by copying the default template from Doxia's Subversion repository, at http://svn.apache.org/viewvc/maven/doxia/doxia-sitetools/trunk/ doxia-site-renderer/src/main/resources/ org/apache/maven/doxia/siterenderer/resources/default-site.vm?view=log to the src/site/site.vm file in the hello-world-site-skin project directory. As you'll notice, this template is fairly involved, so we'll follow the copy-and-tweak methodology that has made open source so successful. Now that we have a basic page template to work with, all we need to do is make a few relatively small changes.

First, locate the menuItem macro. It resides in a section that looks like this:

#macro ( menuItem $item )

  ...
  
#end

TIP: Velocity works a lot like Java, in that it uses nested pairs of start and end markers. Since the end marker (quite literally #end in Velocity) is always the same, indentation is important to keep the code readable. In the example above, look for an #end line that matches the #macro line in indentation.

Now, we'll replace this macro definition with a new one, that injects Javascript references:

#macro ( menuItem $item $listCount )
  #set ( $collapse = "none" )
  #set ( $currentItemHref = $PathTool.calculateLink( $item.href, . ) )
  #set ( $currentItemHref = $currentItemHref.replaceAll( "\\", "/" ) )

  #if ( $item && $item.items && $item.items.size() > 0 )
    #if ( $item.collapse == false )
      #set ( $collapse = "collapsed" )
    #else
      ## By default collapsed
      #set ( $collapse = "collapsed" )
    #end

    #set ( $display = false )
    #displayTree( $display $item )

    #if ( $alignedFileName == $currentItemHref || $display )
      #set ( $collapse = "expanded" )
    #end
  #end
  <li class="$collapse">
    #if ( $item.img )
      #if ( ! ( $item.img.toLowerCase().startsWith("http") || $item.img.toLowerCase().startsWith("https") ) )
        #set ( $src = $PathTool.calculateLink( $item.img, . ) )
        #set ( $src = $item.img.replaceAll( "\\", "/" ) )
        <img src="$src"/>
      #else
        <img src="$item.img" align="absbottom" style="border-width: 0"/>
      #end
    #end
    #if ( $alignedFileName == $currentItemHref )
      <strong>$item.name</strong>
    #else
      #if ( $item && $item.items && $item.items.size() > 0 )
      <a onclick="expand('list$listCount')" style="cursor:pointer">$item.name</a>
          #else
      <a href="$currentItemHref">$item.name</a>
          #end
    #end
  #if ( $item && $item.items && $item.items.size() > 0 )
    #if ( $collapse == "expanded" )
    <ul id="list$listCount" style="display:block">
        #else
    <ul id="list$listCount" style="display:none">
        #end
      #foreach( $subitem in $item.items )
            #set ( $listCounter = $listCounter + 1 )
        #menuItem( $subitem $listCounter )
      #end
    </ul>
  #end
  </li>
#end

Notice that we've added a new parameter to the menuItem macro. We'll also have to change references to this macro, or the resulting template may produce unwanted or internally inconsistent XHTML. To finish changing these references, we have to make a similar replacement in the mainMenu macro. Find this macro by looking for something similar to the following:

#macro ( mainMenu $menus )

  ...

#end

And replacing it with this:

#macro ( mainMenu $menus )
  #set ( $counter = 0 )
  #set ( $listCounter = 0 )
  #foreach( $menu in $menus )
    #if ( $menu.name )
    <h5 onclick="expand('menu$counter')">$menu.name</h5>
    #end
    <ul id="menu$counter" style="display:block">
      #foreach( $item in $menu.items )
        #menuItem( $item $listCounter )
                #set ( $listCounter = $listCounter + 1 )
      #end
    </ul>
    #set ( $counter = $counter + 1 )
  #end
#end

This new mainMenu macro is compatible with the new menuItem macro above, and also provides support for a Javascript-enabled top-level menu.

Next, we must also provide a small modification to the main XHTML template at the bottom of this template file, to introduce the Javascript method referenced in the new menuItem macro. Simply find the section that looks similar to the following:

  <head>
    ...
    <meta http-equiv="Content-Type" content="text/html; charset=${outputEncoding}" />
    ...
  </head>

and replace it with this:

  <head>
    ...
    <meta http-equiv="Content-Type" content="text/html; charset=${outputEncoding}" />
    <script type="text/javascript">
        function expand( item ) {
                var expandIt = document.getElementById( item );
                if( expandIt.style.display == "block" ) {
                        expandIt.style.display = "none";
                        expandIt.parentNode.className = "collapsed";
                } else {
                        expandIt.style.display = "block";
                        expandIt.parentNode.className = "expanded";
                }
        }
    </script>
    #if ( $decoration.body.head )
      #foreach( $item in $decoration.body.head.getChildren() )
        #if ( $item.name == "script" )
          $item.toUnescapedString()
        #else
          $item.toString()
        #end
      #end
    #end
  </head>

Finally, we need to make a minor modification to the hello-world POM, which will configure the site plugin to use our new page template:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-site-plugin</artifactId>
        <configuration>
          <templateDirectory>src/site</templateDirectory>
          <template>site.vm</template>
        </configuration>
      </plugin>
    </plugins>
  </build>
  ...
</project>

Now, you should be able to regenerate your project website.

GOTCHA! Apparently, in cases where the website - or a custom site skin - provides its own Velocity template, Doxia's site-renderer component (version 1.0-alpha-8, brought in by the maven-site-plugin, version 2.0-beta-5) won't use the default maven-base.css file provided by Doxia. To get your website looking right again, you'll need to copy all of the resources except the maven-theme.css file into the hello-world-site-skin project. Perhaps the simplest way to do this is to checkout the Doxia site-renderer project, remove the maven-theme.css file, and copy the rest of the files over, like this:

hello-world$ cd ..
workdir$ svn co \
    http://svn.apache.org/repos/asf/maven/doxia/doxia-sitetools/trunk/doxia-site-renderer
workdir$ rm \
    doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/css/maven-theme.css
workdir$ cp -rf \
    doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/* \
    hello-world/src/site/resources

Then, rebuild the skin and regenerate the site, and your website should look normal again. This issue has documented in Doxia's JIRA under DOXIA-106, found at http://jira.codehaus.org/browse/DOXIA-106.

When you regenerate the site, you'll notice that the Developer Resources menu item now looks just like regular text. This is caused by a quirky interaction between the site's CSS and our new custom page template. It can be fixed by modifying our site.css to restore the proper link color for these menus. Simply add this:

li.collapsed, li.expanded, a:link {
  color:#36a;
}

Once again, regenerate the website. This time, the menu's link color should be corrected. When you click on the Developer Resources link, it will expand the submenu and display the Architecture and Embedding menu-items. Finally, since we've turned the Developer Resources menu-item into a dynamically-folding submenu, we've lost the ability to reach the developer/index.apt page, which is our landing page for this subsection of the website. To restore this, let's modify our site descriptor once more, to add a new menu item for the page:

<project name="Hello World">
  ...
  <menu name="Main Menu">
    ...
    <item name="Developer Resources" collapse="true">
      <item name="Overview" href="/developer/index.html"/>
      <item name="System Architecture" href="/developer/architecture.html"/>
      <item name="Embedder's Guide" href="/developer/embedding.html"/>
    </item>
  </menu>
  ...
</project>

Regenerate the website once more, then click on the Developer Resources menu, and you'll see that order has been restored. Of course, we could be a little more clever, and automatically load the first sub-menu item when this menu is triggered. Then, there are endless small tweaks we could make to this basic template modification. Perfecting this template is a subjective exercise best left to you, the reader.

Reusable Website Skins

Quite often, projects are part of a larger organization. Whether that be an open-source software foundation, some sort of affiliation program, or even the modularization of a larger project, these project's websites often work better when they share a common navigation and over-arching style. Just as it is difficult to preserve such common elements between individual HTML pages, coordinating a series of site.css files from multiple projects represents a serious maintenance burden.

This is where Maven's support for website skinning can save you a lot of pain and suffering. In many cases, one of Maven's alternative website skins can do the job nicely. You can choose from several skins:

  • Maven Classic Skin - org.apache.maven.skins:maven-classic-skin:1.0
  • Maven Default Skin - org.apache.maven.skins:maven-default-skin:1.0
  • Maven Stylus Skin - org.apache.maven.skins:maven-stylus-skin:1.0.1

You can find an up-to-date and comprehensive listing in the Maven repository: http://repo1.maven.org/maven2/org/apache/maven/skins/. These each provide their own layout for navigation, content, logos, etc. However, if none of these fit the bill, you also have the option of creating your own.

Creating a custom skin is a simple matter of wrapping your customized maven-theme.css in a Maven project, so that it can be referenced by groupId, artifactId, and version. It can also include resources such as images, and a replacement website template (written in Velocity) that can generate a completely different XHTML page structure. In most cases, custom CSS can manage the changes you desire. To demonstrate, let's create a designer skin for the hello-world project, starting with a custom maven-theme.css.

Before we can start writing our custom CSS, we need to create a separate Maven project to allow the hello-world site descriptor (not to mention those from other projects) to reference it. First, use Maven's archetype plugin to create a basic project. Issue the following command from the directory above the hello-world project's root directory:

$ mvn archetype:create -DartifactId=hello-world-site-skin -DgroupId=book.sitegen

Just as before, this will create a project (and a directory) called hello-world-site-skin.

NOTE: Since we're working on the custom skin, you should issue all of the following commands from the new hello-world-site-skin directory, unless otherwise directed.

Also as before, Maven will create a skeletal source file and test case for this new project. We don't need these files, but we do need to create a resources directory under src/main to house our skin content:

$ rm -rf src/main/java src/test
$ mkdir src/main/resources

In addition, you may want to remove the dependencies section from the new pom.xml file; we don't have any dependencies for this site skin (since we've deleted the sample source code).

Create a Custom Theme CSS

Now, we're ready to write some CSS for our custom skin. To build on the lessons learned with the site.css file, let's generalize those custom styles here, for use in other projects. Unlike the site.css file, which goes in the site-specific source directory for a project, the maven-theme.css file lives in the src/main/resources/css directory. The reason for the change is that we're building a jar artifact here. Therefore, in order to have the maven-theme.css file included in that jar, it must reside in the main project-resources directory, src/main/resources.

Also, since we're simply attempting to customize the default Maven website theme - called maven-classic-skin - we should begin with the theme file from this skin, then customize it to include our new styles. To get a copy of this theme file, save the contents of http://svn.apache.org/viewvc/maven/skins/trunk/maven-default-skin/src/main/resources/css/maven-theme.css?view=co to src/main/resources/css/maven-theme.css in our new skin project.

Now that we have the base theme file in place, let's customize it using the CSS from our old site.css file. NOTE: You'll need to replace the corresponding CSS in the original theme file with the following:

#navcolumn h5 {
  font-size: smaller;
  border: 1px solid #aaaaaa;
  background-color: #bbb;
  margin-top: 7px;
  margin-bottom: 2px;
  padding-top: 2px;
  padding-left: 2px;
  color: #000;
}

Now, we must install this skin into the local Maven repository before we can reference it:

$ mvn clean install

Once the installation is complete, switch back to the hello-world project directory, and backup the site.css file, to get it out of the way:

$ mv src/site/resources/css/site.css src/site/resources/css/site.css.bak

Then, modify the site descriptor (site.xml, if you remember) to use the new skin:

<project name="Hello World">
  ...
  <skin>
    <groupId>book.sitegen</groupId>
    <artifactId>hello-world-site-skin</artifactId>
  </skin>
  ...
</project>

Regenerate the project website for hello-world, and you should see the same gray-panelled menu headers that we setup in the preceding section. Now, we can go a step further, and incorporate the custom page template that we created previously into the new hello-world-site-skin.

Incorporate a Custom Site Template

Previously, we customized the hello-world project site directly using our own Velocity page template and a minor configuration of the site plugin in the POM. In much the same way, we can customize any number project websites with the same custom template by incorporating it in our new hello-world-site-skin project.

As I mentioned previously, Maven's site plugin makes use of the Doxia rendering engine to combine source documents with a page template, and output XHTML. When reading the page template from a custom skin, Doxia's site-rendering tools will expect to find a file called META-INF/maven/site.vm inside the skin jarfile. So, incorporating the custom page template we developed previously is a simple matter of copying the template file into the correct location within the hello-world-site-skin:

$ cp hello-world/src/site/site.vm \
    hello-world-site-skin/src/main/resources/META-INF/maven

Also, since we're not using the per-project page template any more, we should remove the reference to it in the hello-world POM. Remove or comment out the following section:

      <plugin>
        <artifactId>maven-site-plugin</artifactId>
        <configuration>
          <templateDirectory>src/site</templateDirectory>
          <template>site.vm</template>
        </configuration>
      </plugin>

The src/site/site.vm file in the hello-world project isn't used any longer. It will not hurt to leave it where it is, but neither will it hurt to remove it. The same goes for the src/site/resources/css/site.css.bak file.

Finally, remember the workaround for DOXIA-106. We'll need to move these files (which are now polluting our hello-world project unnecessarily) into the new hello-world-site-skin's src/main/resources directory:

$ cd ..
$ mkdir -p hello-world-site-skin/src/main/resources/css
$ mv hello-world/src/site/resources/css/maven-base.css \
    hello-world-site-skin/src/main/resources/css
$ mkdir -p hello-world-site-skin/src/main/resources/images
$ mv hello-world/src/site/resources/images/logos \
    hello-world-site-skin/src/main/resources/images
$ mv hello-world/src/site/resources/images/expanded.gif \
    hello-world-site-skin/src/main/resources/images
$ mv hello-world/src/site/resources/images/collapsed.gif \
    hello-world-site-skin/src/main/resources/images

Now, we can rebuild the site skin, and regenerate the hello-world website. If you refresh your browse, and find that you're wondering what happened to the color of the Developer Resources menu, remember that we modified the site.css file to accommodate the new page template. To carry this behavior over to the new skin, we simply perform the same modification on the maven-theme.css file. Change this:

a:link {
  ...
}

to this:

li.collapsed, li.expanded, a:link {
  ...
}

Rebuild the skin, then regenerate the website, and you'll see that the Developer Resources menu has returned to normal.

Tips and Tricks

In addition to the techniques used to create a basic project website and fill it in with content, there are some useful tips that you should know. Each of the following items can be used independently of the rest. For this reason, it's not necessary that you read this section straight through; think of it as a reference for those obscure-but-useful site-plugin details.

Inject HTML into <HEAD/> of Each Page

Occasionally, it would be nice to have the ability to inject a small amount of XHTML into the head of each page on the generated website. This could be useful if we wanted to add an RSS link to the news page of our hello-world project, for instance. To do this, Maven supports the following section in the site descriptor:

<project name="Hello World">
  ...
  <body>
    <head>
      <link href="http://dev.example.com/sites/hello-world/feeds/news"
            type="application/atom+xml" 
            id="auto-discovery" 
            rel="alternate" 
            title="Hello-World News" />
    </head>
    ...
  </body>
</project>

GOTCHA! Don't be confused by the fact that the head section goes inside the body section in the site descriptor. Apparently, the site descriptor is not meant to represent an abstracted page template, despite the common terminology.

Add Links under Your Site Logo

If your project has an affiliation with one or more other projects, you may want to display persistent links to those projects prominently on your project website. A good example of this is Codehaus Mojo website, at http://mojo.codehaus.org. This site hosts Maven plugins that, for one reason or another, cannot be hosted on Maven's main plugin website. Intuitively, it makes sense that users looking at these plugins might want to know more about Maven itself. To make their lives easier, the Mojo website contains a link to the Maven project on the right side of the bar under the site logo. This link is always in the same place, regardless of where you are in the Mojo website. It gives the user instant access to other websites that have a close, integral relationship with the current one.

In the nomenclature of the site descriptor, these are simply called links. To add a links section to your project website, simply modify your site descriptor to include something similar to this:

<project name="Hello World">
  ...
  <body>
    ...
    <links>
      <item name="Sibling Project" href="http://dev.example.com/sites/sibling"/>
      <item name="Apache Maven Doxia" href="http://maven.apache.org/doxia"/>
    </links>
    ...
  </body>
</project>

Add Breadcrumbs to Your Site

When your project's website makes up only a subsection of another, encompassing website, it often helps users to know how to navigate back up to the outer site. This can often come about when the project is hosted as part of a larger community, where the over-arching main page may provide classifications, searchability, and other cross-project features.

Such breadcrumb links are easy to configure for your project website. Simply add a new section to the body of your site descriptor that looks similar to this:

<project name="Hello World">
  ...
  <body>
    ...
    <breadcrumbs>
      <item name="Example Software Foundation" href="http://www.example.com"/>
      <item name="ESF Projects" href="http://dev.example.com"/>
    </breadcrumbs>
    ...
  </body>
</project>

NOTE: The item entries in the breadcrumbs section are listed in parent-to-child order, to symbolize that each successive link is a sub-site of the last.

Add the Project Version

Often, it's helpful to give your users a clear indication of which version of your project is documented by a particular website. This is particularly useful when used in combination with the Maintain Documentation for Multiple Project Versions tip, where documentation for multiple project versions is maintained side-by-side. To display your project's version on the website, simply add the following to your site descriptor:

<project name="Hello World">
  ...
  <version position="left"/>
  ...
</project>

This will position the version (in the case of the hello-world project, it will say "Version: 1.0-SNAPSHOT") in the upper left-hand corner of the site, right next to the default "Last Published" date. Valid positions for the project version are:

  • left Left side of the bar just below the site logo
  • right Right side of the bar just below the site logo
  • navigation-top Top of the menu
  • navigation-bottom Bottom of the menu
  • none Suppress the version entirely

Modify the Publication Date Format and Location

In some cases, you may wish to reformat or reposition the "Last Published" date for your project website. Just like the project version tip above, you can specify the position of the publication date by using one of the following:

  • left Left side of the bar just below the site logo
  • right Right side of the bar just below the site logo
  • navigation-top Top of the menu
  • navigation-bottom Bottom of the menu
  • none Suppress the publication date entirely

Now, add the publication date element, using the position you selected above, like this:

<project name="Hello World">
  ...
  <publishDate position="navigation-bottom"/>
  ...
</project>

By default, the publication date will be formatted using MM/dd/yyyy. You can change this format by using the standard notation found in the JavaDocs for java.text.SimpleDateFormat (see http://java.sun.com/j2se/1.5.0/docs/api/java/text/SimpleDateFormat.html for more information). To reformat the date using yyyy-MM-dd, use the following:

<project name="Hello World">
  ...
  <publishDate position="navigation-bottom" format="yyyy-MM-dd"/>
  ...
</project>

Summary

The instructions in this chapter have guided you through the process of creating a customized project website, using the maven-site-plugin. We wrote documentation for a sample project in various source formats, and watched as the Doxia rendering engine transformed our source documents into XHTML. In addition, we investigated how to tailor the many options supported by the site plugin. Among these were navigational menus, breadcrumbs and other special links, and even reusable website skins that are capable of applying a custom look-and-feel - including logos, javascript, CSS, and more - to a set of websites. Although Maven's documentation-rendering features make maintaining such documentation much simpler, this represents only one aspect of the website-generation capabilities present in Maven's site plugin.

Resources


Previous: Chapter 9