Java
Article
By Graham Cox

Building your Front End with Maven: Simple Resources

By Graham Cox
Help us help you! You'll get a... FREE 6-Month Subscription to SitePoint Premium Plus you'll go in the draw to WIN a new Macbook SitePoint 2017 Survey Yes, let's Do this It only takes 5 min

Every time you are developing a web application, you will invariably have a number of static resources that you desire to serve up to the end user. These static files come in a number of different forms – HTML, CSS, LESS, SCSS, Javascript, Plain Text, Markdown, Asciidoc, etc. – and have a number of challenges to best incorporate into your web application for the easiest development process. This article aims to show some simple techniques using Maven plugins to streamline the development and incorporation of these static resources into your application.

Serving up Static Resources

It is assumed that you are already able to serve up static resources from your web application. Generally, whichever framework you are using to build your application has standard ways of supporting this – Spring, for example – uses the mvc:resources tag. Additionally, assuming you are using a Servlet Container such as Tomcat, it is often the case that you can serve anything up that appears in the src/main/webapp directory without any extra configuration at all. It is important that you know where in the resulting WAR file your static files need to end up, as this will be used repeatedly throughout the examples given in this article.

Plain, Unmanipulated Files

The absolute simplest form of static resources that can be included are ones that require absolutely no manipulation. These are files that you write and are then included into the webapp as-is. Including these is really simple. All you need to do is put the files into either src/main/webapp or src/main/resources as appropriate for where you want them to appear. Files included in src/main/webapp will be copied into the root of your WAR file, whereas files included in src/main/resources will be copied into target/classes, which then ends up on the classpath of your webapp.

Templated Files

Sometimes you find that you want to have some plain files, but include in them expanded properties taken from the Maven build. For example, the version number of the artifact is a common one that might be included.

This is achievable using standard Maven plugins that are already used as part of your build – the Maven Resources Plugin and the Maven WAR Plugin – so let’s have a look at them.

Maven Resources Plugin

Without any additional configuration at all, the Maven Resources Plugin is already used to copy the src/main/resources directory into the resultant JAR or WAR file. (Note, the Maven Resources Plugin is also used for the src/test/resources directory, and everything that is mentioned here is equally applicable to that).

By default, it will not do filtering, though, so some extra configuration is needed to support this. Filtering is the act of substituting special placeholders in your static resources with the correct values at the time the resources are copied into your webapp.

Configuring filtering of your resources is a simple case of adding the following configuration to your pom.xml file:

<build>
    <resources>
        <resource>
            <directory>src/main/filteredResources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

This block has two effects:

  • Adds a new directory from which resources will be copied – in this case we used src/main/filteredResources
  • Enable filtering for all resources in this directory

It is recommended that you keep your filtered resources separate if possible, so that you don’t accidentally perform filtering on files you don’t mean to do so. For example, Spring Context files often contain properties to be expanded that use the exact same syntax, but that you do not wish to be expanded by this process.

Filtering is then achieved by adding special placeholders into your files. These placeholders all start ${ and end }. Between the braces is the property name that you wish to substitute into the file. Say, for example, you have a file src/main/filteredResources/details.txt that contains the following:

Group ID: ${project.groupId}
Artifact ID: ${project.artifactId}
Version: ${project.version}

Then, upon copying into the built webapp, this will be automatically expanded with the group ID, artifact ID and version of the project currently being built.

The properties that can be used here are anything available to the Maven Reactor. These include the default Maven properties, additional ones defined in the POM file, and system properties provided on the command line.

Maven WAR Plugin

The Maven WAR Plugin is slightly less integrated into the build process, so configuring it needs to use the standard mechanism for configuring plugins.

Again, by default this will not do any filtering. As before, you probably want to be selective about the files that are chosen for filtering, so as to not interfere too much with other sources. If you wish to use a similar setup to before, where you have one entire directory for filtered resources and one for unfiltered ones, then the following configuration will work.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <configuration>
                <webResources>
                    <resource>
                        <directory>src/main/filteredWebapp</directory>
                        <filtering>true</filtering>
                    </resource>
                    <resource>
                        <directory>src/main/webapp</directory>
                        <filtering>false</filtering>
                    </resource>
                </webResources>
            </configuration>
        </plugin>
    </plugins>
</build>

As before, we now have two directories from which files will be included into the WAR file. The files in src/main/webapp will be included as-is, whilst the files in src/main/filteredWebapp will be filtered as they are copied across.

Alternatively, you may want to keep all of the files together, and selectively filter only a small subset of them. This can be achieved as follows:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <configuration>
                <webResources>
                    <resource>
                        <directory>src/main/webapp</directory>
                        <filtering>true</filtering>
                        <includes>
                            <include>version.html</include>
                        </includes>
                    </resource>
                    <resource>
                        <directory>src/main/webapp</directory>
                        <filtering>false</filtering>
                        <excludes>
                            <exclude>version.html</exclude>
                        </excludes>
                    </resource>
                </webResources>
            </configuration>
        </plugin>
    </plugins>
</build>

This version keeps all of the files in src/main/webapp, and selectively filters only the specified files – in this case version.html. This makes it easier to keep track of which files are in use, but the downside is that the list of filtered files needs to be specified twice – once to include in the filtered set of files and once to be excluded from the non-filtered set. This is an unfortunate side effect of the need to use different resource tags to represent filtered and unfiltered resources.

Using Maven to process your project's resources

--ADVERTISEMENT--

CSS Files

Any webapp will invariably need to include Cascading Stylesheet (CSS) files to describe the styling of the page. These CSS files can be built in a variety of ways, ranging from hand-crafted and included as simple resources – as described above – to using a CSS preprocessor such as LESS or SASS.

LESS

LESS files can be supported by use of the LESS Maven Plugin. LESS is implemented as a Javascript application, and the LESS Maven Plugin incorporates this into the build by executing the Javascript inside of the JVM running the build. This plugin is slightly simpler and lighter weight than the SASS one, but LESS is also a slightly simpler preprocessor with less features. If you’ve never used a CSS preprocessor before it is a good place to start, though.

Configuring the build to automatically build LESS files into CSS files can be done as follows:

<build>
    <plugins>
        <plugin>
            <groupId>org.lesscss</groupId>
            <artifactId>lesscss-maven-plugin</artifactId>
            <version>1.7.0.1.1</version>
            <configuration>
                <sourceDirectory>
                    ${project.basedir}/src/main/less
                </sourceDirectory>
                <outputDirectory>
                    ${project.build.directory}/${project.build.finalName}/css
                </outputDirectory>
            </configuration>
            <executions>
                <execution>
                    <id>compile-less</id>
                    <phase>generate-resources</phase>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

This configuration will compile every *.less file in the src/main/less directory, and place the resulting CSS files in the css directory of the webapp, exactly the same as if the CSS files themselves had been put into src/main/webapp/css. If you wish to include the files on the classpath – as if they had been written in src/main/resources then simply change outputDirectory to use classes instead of ${project.build.finalName}.

SCSS

SCSS files can be supported by use of the SASS Maven Plugin. SASS is implemented as a Ruby application, and the SASS Maven Plugin incorporates this into the build by executing the Ruby code using the JRuby bindings for Java. This makes the process slightly more complicated than the LESS plugin but the complexity is mostly handled by the Maven plugin and is not something the user needs to care about.

Configuring the build to automatically build SCSS files into CSS files can be done as follows:

<build>
    <plugins>
        <plugin>
            <groupId>nl.geodienstencentrum.maven</groupId>
            <artifactId>sass-maven-plugin</artifactId>
            <version>2.23</version>
            <executions>
                <execution>
                    <id>lint</id>
                    <phase>validate</phase>
                    <goals>
                        <goal>scss-lint</goal>
                    </goals>
                </execution>
                <execution>
                    <id>build</id>
                    <phase>generate-resources</phase>
                    <goals>
                        <goal>update-stylesheets</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <sassSourceDirectory>
                    ${project.basedir}/src/main/scss
                </sassSourceDirectory>
                <destination>
                    ${project.build.directory}/${project.build.finalName}/css
                </destination>
            </configuration>
        </plugin>
    </plugins>
</build>

This configuration will compile every *.scss file in the src/main/scss directory, and place the resulting CSS files in the css directory of the webapp, exactly the same as if the CSS files themselves had been put into src/main/webapp/css. If you wish to include the files on the classpath – as if they had been writtein in src/main/resources then simply change destination to use classes instead of ${project.build.finalName}.

Note that one feature the SASS plugin gives over the LESS plugin is support for linting of the stylesheets. This is configured above to execute in an earlier phase and will ensure that the SCSS files are syntactically valid. This can be very useful to ensure that the build is going to be valid early in the build lifecycle.

Generated HTML Files

Occasionally you might have a need to generate HTML out of markup files that you have as part of your build. For example, you might maintain API Documentation in Markdown or AsciiDoc format, and want to publish HTML versions of this as part of the deployed webapp. (It would even be possible to generate these from your code and then use this plugin to convert this into HTML. Doing this is left as an exercise to the reader.)

Markdown Files

Markdown files can be converted into HTML files by use of the Markdown Page Generator Plugin. This plugin will automatically generate HTML files for all Markdown files that are processed. It can be configured to fully customize the style of the generated files but out of the box it still generates perfectly usable files.

Configuring the build to automatically build Markdown files into HTML files can be done as follows:

<build>
    <plugins>
        <plugin>
            <groupId>com.ruleoftech</groupId>
            <artifactId>markdown-page-generator-plugin</artifactId>
            <version>1.0.0</version>
            <executions>
                <execution>
                    <phase>process-sources</phase>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <inputDirectory>
                    ${project.basedir}/src/main/markdown
                </inputDirectory>
                <outputDirectory>
                    ${project.build.directory}/${project.build.finalName}/docs
                </outputDirectory>
            </configuration>
        </plugin>
    </plugins>
</build>

This configuration will process every *.md file in src/main/markdown and produce HTML files in the docs directory of the webapp, exactly the same as if the HTML files themselves had been put into src/main/webapp/docs.

AsciiDoc files

AsciiDoc files can be converted into HTML files by use of the Asciidoctor Maven Plugin. This plugin will automatically generate HTML files for all AsciiDoc files that are processed. It can be configured to fully customize the style of the generated files but out of the box it still generates perfectly usable files.

Configuring the build to automatically build AsciiDoc files into HTML files can be done as follows:

<build>
    <plugins>
        <plugin>
            <groupId>org.asciidoctor</groupId>
            <artifactId>asciidoctor-maven-plugin</artifactId>
            <version>1.5.5</version>
            <executions>
                <execution>
                    <phase>process-resources</phase>
                    <goals>
                        <goal>process-asciidoc</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <sourceDirectory>
                    ${project.basedir}/src/main/asciidoc
                </sourceDirectory>
                <outputDirectory>
                    ${project.build.directory}/${project.build.finalName}/docs
                </outputDirectory>
                <preserveDirectories>true</preserveDirectories>
                <backend>html5</backend>
            </configuration>
        </plugin>
    </plugins>
</build>

This configuration will process every *.ad, *.adoc and *.asciidoc file in src/main/asciidoc and produce HTML files in the docs directory of the webapp, exactly the same as if the HTML files themselves had been put into src/main/webapp/docs.

Summary

Whether it is filtering some templated resource files to add build information, copying CSS files, processing LESS or SCSS files, or transforming Markdown an Asciidoc to HTML, there is a Maven plugin for everything. Each of them have a lot more depth than can be covered here, so if you want to do more with them, it’s likely possible with some configuration work. However, the above configuration examples are usable out of the box to add support for these technologies as a standard part of the build.

Hopefully this has given some ideas on ways to streamline the inclusion of front-end assets in your web application, without the need for complicated multi-stage builds.

Login or Create Account to Comment
Login Create Account
Recommended
Sponsors
Get the most important and interesting stories in tech. Straight to your inbox, daily.Is it good?