Build your static website or blog using JBake (” MVN generate-resources “). Use layouts, macros, and data files.

We migrated the entire www.optaplanner.org site (1399 files) to build using Java and Maven instead of Ruby and Rake. On the surface, nothing has changed. But in the source code, it was a game changer for our team of Java developers.

Our Java team can now easily contribute to the site. Within a few hours of completing the migration, one of our developers had already submitted a commit that didn’t want to touch the previous source code with a ten-foot pole.

We built this website. We built the site on Java and Maven. We built this website. We built this site at JBake and Freemarker.

Why use a static website generator?

The Static Website Builder converts templates and content files into static HTML/JS/CSS websites. For a project like ours, this has many advantages over a content management system (CMS) :

Hosting is cheap. The GitHub page even hosts static sites for free.

The source files enter Git for backup and historical records.

Source file is in plain text format:

Changes come in the form of pull requests for appropriate review and CI validation.

The source code is open in our IDE, which encourages refactoring along with the code. This will reduce stale content.

Awestruct has been working for us for years. But due to the lack of activity, it’s time to upgrade.

Why jake?

Because we are Java programmers.

There are several good static website generators, such as Jekyll (Ruby) and Hugo (Go). We chose JBake (Java) because:

Our site is now built using Maven (MVN Generate-Resources).

You don’t need to install anything. It’s not even jake. Everyone uses the same version of a JBake build, such as pom.xml.

And it’s fast: even MVN Clean can build 150 output pages on my machine in 20 seconds.

.below is all Java.

Writing conditional expressions is simple. API ( String.substring(), …) Very familiar with. The date format (DMMMM YYYY) and the regular expression behave as expected.

Most importantly, the error message is clear.

For eight years, I’ve written this site in AweStruct (Ruby). But I never took the time to learn Ruby properly, so each change required hours of trial and error. I can’t just read the error message and fix it. It’s not Ruby’s fault. That’s because I never really spent a few days learning Ruby. With JBake, I can fix mistakes in a fraction of the time: no more trial and error.

What is Jake?

JBake is a static website builder with lots of options:

Use Maven or Gradle to build.

We chose Maven because all of our repos are built with Maven (although two OptaPlanner QuickStarts are also built with Gradle because OptaPlanner also supports Gradle).

Write content in ASCIIDOC, Markdown, or HTML.

We chose Aciidoc because it is richer and more reliable than Markdown. In addition, all of our documentation is written in ASCIIDOC.

Create templates using Freemarker, Thymeleaf, or Groovy.

We chose Freemarker because it is a powerful, battle-tested templating engine.

Tips and Tricks

These are common tasks for building advanced static websites and how to implement each task in jbake-freemarker. You can even call these JBake design patterns:

Use macros to render shared content

Almost all of our templates show the same panel with the latest version:

The latest release

Freemarker templates are great for avoiding repeating themselves (DRY) : templates/macros.ftl using a macro that outputs HTML:

<#macro latestReleases>
    <div class="panel panel-default">
        <div class="panel-heading">Latest release</div>
        ...
    </div>
</#macro>

Then use it in the *.ftl template:

<#import "macros.ftl" as macros>
...
<div class="row">
    <div class="col-md-9">
        ...
    </div>
    <div class="col-md-3">
        <@macros.latestReleases/>
    </div>
</div>

Use data files to add video, events, or other volatile data

Some data changes are too frequent to be maintained in a storage or template file:

A data file, such as a simple *.yml file, is a good place to store volatile data like this:

Create the data/videos. Yml:

- youtubeId: blK7gxqu2B0
title: "Unit testing constraints"
...

- youtubeId: gIaHtATz6n8
title: "Maintenance scheduling"
...

- youtubeId: LTkoaBk-P6U
title: "Vaccination appointment scheduling"
...

Then use it in the FTL template:


<#assign videos = data.get('videos.yml').data>

<div class="panel panel-default">
    <div class="panel-heading">Latest videos</div>
    <div class="panel-body">
        <ul>
            <#list videos[0..6] as video>
                <li>
                    <a href="https://youtu.be/${video.youtubeId}">${video.title}</a>
                </li>
            </#list>
        </ul>
    </div>
</div>

Layout of the inheritance

All HTML pages typically share the same HTML header (metadata), header (navigation), and footer. These are well suited to base.ftl layouts and are extended by all the other templates:



Although most of the content uses normalbase.ftl, all usecasebase.ftl use case pages have separate templates, such as vehicle routing issues (VRP), maintenance plans, and shift schedules.

Build layout inheritance with the macro with <\# Nested > :

Create templates/base. FTL:


<#macro layout>
    <html>
        <head>
          ...
        </head>
        <body>
            <div>
                ... <#-- header -->
            </div>
            <#nested>
            <div>
              ... <#-- footer -->
            </div>
        </body>
    </html>
</#macro>

Extend it templates/useCaseBase FTL and introduce related_tag custom attributes:

<#import "base.ftl" as parent> <@layout>${content.body}</@layout> <#macro layout> <@parent.layout> <h1>${content.title}</h1> <#nested> <h2>Related videos</h2> <#assign videos = data.get('videos.yml').data> <#assign relatedVideos = videos? filter(video -> video.tags.contains(content.related_tag))> <ul> <#list relatedVideos as video> <li><a href="https://youtu.be/${video.youtubeId}">${video.title}</a></li> </#list> </ul> </@parent.layout> </#macro>

Create the content/vehicleRoutingProblem adoc using the template page and set the related_tag properties of use cases:


= Vehicle Routing Problem
:jbake-type: useCaseBase
:jbake-related_tag: vehicle routing

The Vehicle Routing Problem (VRP) optimizes the routes of delivery trucks,
cargo lorries, public transportation (buses, taxi's and airplanes)
or technicians on the road, by improving the order of the visits.
This routing optimization heavily reduces driving time and fuel consumption compared to manual planning:

...

Start trying it yourself. To build the www.optaplanner.org website, run the following command:

$ git clone https://github.com/kiegroup/o… . $ cd optaplanner-website $ mvn clean generate-resources … $firefox target/website/index. The HTML or look at the source code.