How to build Apache Sling projects using Maven

In the previous post, we had a look at some of the samples included with the Sling framework.

In this post, I'll walk you through the steps I followed when setting up an Apache Sling project that uses Maven.

Note: Apache Sling is part of the Adobe Experience Manager (AEM) stack.

Prerequisites

  • OpenJDK for Java 1.8
  • Git
  • Maven (I'm using 3.3.9)
  • The Eclipse IDE for Java EE Developers (I'm using Neon)
  • The Sling Launchpad

Note: This post will walk you through the steps required to install the OpenJDK for Java 1.8. This post will walk you through the steps required to install Git, Maven and the Eclipse IDE. And, this post introduces the Apache Sling Launchpad.

Getting Started

When developing a Sling project you usually create both user interface components and OSGi services. And, these are typically divided into seperate modules.

A multi-module Maven project

A multi-module Maven project is defined by a parent POM referencing one or more modules. We can create a parent POM (and a directory for our new project) by using the pom-root Maven archetype as follows:

mvn archetype:generate \
    -DarchetypeGroupId=org.codehaus.mojo.archetypes \
    -DarchetypeArtifactId=pom-root \
    -DarchetypeVersion=RELEASE \
    -DgroupId=org.robferguson.sling.project \
    -DartifactId=sling-multi-module-maven-project \
    -Dversion=1.0.0-SNAPSHOT \
    -DinteractiveMode=false

The pom-root archetype will create a directory for our new project (i.e., /sling-multi-module-maven-project) and a (very basic) parent POM:

<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>
  <groupId>org.robferguson.sling.project</groupId>
  <artifactId>sling-multi-module-maven-project</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>

</project>

The parent POM is the ideal place to store shared configuration information, so let's update it as follows:

<?xml version="1.0" encoding="UTF-8"?>

  ...

  <properties>
      <sling.username>admin</sling.username>
      <sling.password>admin</sling.password>
  </properties>

  <modules>
  </modules>

</project>

Apache Sling Maven Archetypes

Apache Sling includes several Maven archetypes designed to help kickstart Sling projects.

Now that we have a parent POM, we can use the sling-initial-content-archetype to create a "ui" (where our initial content and scripts are located) module:

mvn archetype:generate \
    -DarchetypeGroupId=org.apache.sling \
    -DarchetypeArtifactId=sling-initial-content-archetype \
    -DgroupId=org.robferguson.sling.project \
    -DartifactId=ui \
    -Dversion=1.0.0-SNAPSHOT \
    -Dpackage=org.robferguson.sling.project.ui \
    -DappsFolderName=project \
    -DartifactName="ui" \
    -DpackageGroup="ui" \
    -DinteractiveMode=false

This is what the sling-initial-content-archetype's generated project structure looks like:

├── /ui
    └── /src
        └── /main
            └── /resources
                └── /SLING-INF
                    └── /content
                        ├── my.first.node.xml
                    └── /nodetypes
                        ├── nodetypes.cnd
                    └── /scripts
                        ├── html.esp
    └── /target

And, we can use the sling-bundle-archetype to create a "core" (where Java files that are used in OSGi services and Sling servlets are located) module:

mvn archetype:generate \
    -DarchetypeGroupId=org.apache.sling \
    -DarchetypeArtifactId=sling-bundle-archetype \
    -DgroupId=org.robferguson.sling.project \
    -DartifactId=core \
    -Dversion=1.0.0-SNAPSHOT \
    -Dpackage=org.robferguson.sling.project.core \
    -DappsFolderName=project \
    -DartifactName="core" \
    -DpackageGroup="core" \
    -DinteractiveMode=false

This is what the sling-bundle-archetype's generated project structure looks like:

├── /core
    └── /src
        └── /main
            └── /java
                └── /org
                    └── /robferguson
                        └── /sling
                            └── /project
                                └── /core
                                    ├── SimpleDSComponent.java
    └── /target

Note: The Sling archetypes will also update the parent POM:

<?xml version="1.0" encoding="UTF-8"?>

  ...

  <properties>
      <sling.username>admin</sling.username>
      <sling.password>admin</sling.password>
  </properties>

  <modules>
      <module>ui</module>
      <module>core</module>
  </modules>

</project>

Depending on the complexity of your project, you may want to refine this setup and create separate modules for individual areas of your project. For example, it is common practice to have separate bundles for infrastrutural components (e.g., loggers) and business components (e.g., workflow steps).

References: