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 often create both user interface components and OSGi services. And, these are typically divided into separate 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:
<?xml version="1.0" encoding="UTF-8"?>
<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"?>
<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">
...
<properties>
<sling.username>admin</sling.username>
<sling.password>admin</sling.password>
</properties>
<modules>
</modules>
</project>
Apache Sling Maven Archetypes
Apache Sling includes several Maven archetype designed to help kickstart Sling projects.
The Sling Initial Content Archetype
Now that we have a parent POM, we can use the sling-initial-content-archetype
to create a "ui" module (where our initial content and scripts are located):
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 (place your initial content here)
├── my.first.node.xml
└── /nodetypes
├── nodetypes.cnd
└── /scripts (place your scripts here)
├── html.esp
└── /target
├── pom.xml
Initial Content Loading
Like me you probably want to provide your application with some initial content, that's why Apache Sling provides support for both registering node types and loading initial content into a repository.
The scaffolding generated by the sling-initial-content-archetype
includes a Node Type Definition File (nodetypes.cnd
):
[my:node] > nt:unstructured
- title (string)
- description (string)
And, a XML Descriptor File (my.first.node.xml
):
<node>
<primaryNodeType>my:node</primaryNodeType>
<property>
<name>title</name>
<type>String</type>
<value>My first node</value>
</property>
<property>
<name>description</name>
<type>String</type>
<value>This node has been created by a sling bundle.</value>
</property>
</node>
Note: Apache Sling also provides support for JSON Descriptor Files.
If you take a look at the "ui" module's POM:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
...
<parent>
<artifactId>sling-multi-module-maven-project</artifactId>
<groupId>org.robferguson.sling.project</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<version>${maven-bundle-plugin.version}</version>
<configuration>
<instructions>
<Sling-Nodetypes>
SLING-INF/nodetypes/nodetypes.cnd
</Sling-Nodetypes>
<Sling-Initial-Content>
SLING-INF/scripts;overwrite:=true;uninstall:=true;path:=/apps/my/node,
SLING-INF/content;overwrite:=true;uninstall:=true;path:=/content
</Sling-Initial-Content>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
...
</project>
You'll notice that the configuration section of the maven-bundle-plugin
references the node type definition file (SLING-INF/nodetypes/nodetypes.cnd
) and the initial content path (SLING-INF/scripts
and SLING-INF/content
).
You can of course create content using tools like curl:
curl -u admin:admin -F":operation=import" -F":contentType=json" \
-F":contentFile=@posts.json" http://localhost:8080/content
To retrieve the posts.json file:
curl http://localhost:8080/content/posts.json
To delete the posts.json file:
curl -u admin:admin -F":operation=delete" \
http://localhost:8080/content/posts
The Sling Bundle Archetype
Now, lets use the sling-bundle-archetype
to create a "core" module (where Java files that are used in OSGi services and Sling servlets are located):
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
├── pom.xml
The Sling archetypes will also update the parent POM:
<?xml version="1.0" encoding="UTF-8"?>
<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">
...
<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:
- Apache Sling docs: Maven Archetypes
- Apache Sling docs: Sling IDE tooling for Eclipse User Guide
- Apache Sling docs: Content Loading
- Apache Sling docs: Manipulating Content
- Sergei Müller: Adobe CQ5/AEM Curl Commands
- Stack Overflow: How to create an empty multi-module Maven project