Create an AEM (CQ) project using Maven


This article is mainly focused on setting up only project structure for CQ/AEM project using maven and guides you through how you can do your day to day development of AEM/CQ project with eclipse. In next article we’ll see how to develop templates, components and other things in details.

Before you go through this article it highly recommended that you have fair knowledge of Maven and at least folder structure of an AEM/CQ project. If you are in hurry, I’ll try to provide a very high level explanation of these two things but, I’ll recommend you to explore that in detail.

1) Typical AEM/CQ project: any web application is mainly composed of view (HTML, JSP etc.), CSS, JavaScript and some server side code. An AEM application is nothing different than this. A typical AEM application will have following folder structure:
  • /apps (Top level folder that is parent for all the code that you’ll develop)
  • /apps/[YOUR_APP_NAME]/components/[CQ templates and components] (contains mostly JSP or other scripts responsible for rendering) 
  • /apps/[YOUR_APP_NAME]/config (contais configuration for a project)
  • /apps/[YOUR_APP_NAME]/install (contains bundles/jar for server side logic) /etc/design/[design assets for your site] (Typically all CSS, JavaScript etc. are stored here and access using CQ Client lib. More information about ClientLibs can be found here: http://suryakand-shinde.blogspot.in/2012/01/clientlib-cq-static-resource-management.html) 
  • /content/dam/[images, docs, any other digital assets] 
  • /content/[YOUR_SITE_ROOT]
2) Maven is a tool for managing project dependences, build management and automation framework. There are many maven plugin available in market to automate various tasks. In this article also we’ll use few maven plug-ins to automate the task of CQ package creation & installation etc.

Before we go into details, let’s see few benefits of configuring CQ project with maven and doing development in eclipse:
  1. You can break your project in to smaller modules that can be managed individually. 
  2. You can do build automation for maven projects easily using Jenkins etc. 
  3. Unit testing becomes easier. Writing test cases becomes easier when you have broken down your project in separate modules (components, templates in one module and you java code for bundles in another module)
Before we start creating actual project, following installations should be there on your machine:
  1. Maven 
  2. CQ/AEM author environment 
  3. Eclipse with Vault Plug-in (VLT Plug-in for eclipse can be downloaded from http://sourceforge.net/projects/vaultclipse) 

Create AEM/CQ Maven Project

Source code for this project can be found here: https://suryakand.googlecode.com/svn/aem/simple-aem-maven-project

We have seen some benefits of maven for CQ projects and have basic knowledge of CQ project structure by now. Typically, a CQ/AEM maven project will be a modular maven project with multiple modules. For this article we’ll create a top level project that contains two modules “bundles” and “content”.

Follow these steps to create a maven project:
Step 1: create a maven project using adobe aem's maven archetype. When you run the command it’ll as for project information on command prompt that you’ll need to provide:
mvn archetype:generate -DarchetypeGroupId=com.day.jcr.vault -DarchetypeArtifactId=multimodule-content-package-archetype -DarchetypeVersion=1.0.2 -DarchetypeRepository=adobe-public-releases

This will create a maven project with two modules one for bundle and another one for content as shown in Fig: AEM eclipse project. a) content: this is where you'll put your templates, components etc. b) bundle: all java code that is responsible for creating bundle will go here.

Step 2: Verify project After the project is created, run the following command just to make sure that everything generated as expected. The build should complete without any error:
mvn clean install

Step 3: Import project into eclipse Once the build is successful, import the project in to eclipse using File-> Import -> Existing Maven Projects -> browse to the directory where you have parent pom.xml file -> Finish


 Fig. 1: Typical AEM/CQ project structure

Step 4: Import project into CQ Now that we have a project ready, next thing that we’ll do is try to install the project (content and bundle) in local author (http://localhost:4502). Execute following command from the directory where we have parent project’s pom.xml file:
mvn clean install –PautoInstallPackage

NOTE: “autoInstallPackage” is a maven profile that is defined in “content” project’s pom.xml file. Go to the CRXDLite and check whether the apps folder contains your project or not?

Most of the stuff has been done for us by maven plug-in but, it is important to understand what is going on behind the scene before we start our day to day development so, let’s see what maven did for us. As evident from the screen shot above maven created a parent project with 2 module projects (bundle and content) for us. “bundle” and “content” are also maven project and they have their own pom.xml file. Let’s look at each pom.xml carefully:

1) pom.xml of parent project: See the comments/NOTE in below XML file for more information about various sections and their usage. I have omitted some part of XML to keep it short so that we can focus on important sections:

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelversion>4.0.0</modelversion>
 <groupid>com.surya.aem</groupid>
 <artifactid>blog-sample</artifactid>
 <version>1.0-SNAPSHOT</version>
 
 <packaging>pom</packaging>

 <name>Blog Sample - Reactor Project</name>
 <description>Maven Multimodule project for Blog Sample.</description>

 <prerequisites>
  <maven>3.0.2</maven>
 </prerequisites>

 
 <properties>
  <crx .host="">localhost</crx>
  <crx .port="">4502</crx>
  <crx .username="">admin</crx>
  <crx .password="">admin</crx>
  <publish .crx.host="">localhost</publish>
  <publish .crx.port="">4503</publish>
  <publish .crx.username="">admin</publish>
  <publish .crx.password="">admin</publish>
  <project .build.sourceencoding="">UTF-8</project>
  <project .reporting.outputencoding="">UTF-8</project>
 </properties>

 <dependencymanagement>
  <dependencies>
   
  </dependencies>
 </dependencymanagement>

 <repositories>
  <repository>
   <id>adobe</id>
   <name>Adobe Public Repository</name>
   <url>http://repo.adobe.com/nexus/content/groups/public/</url>
   <layout>default</layout>
  </repository>
 </repositories>
 <pluginrepositories>
  <pluginrepository>
   <id>adobe</id>
   <name>Adobe Public Repository</name>
   <url>http://repo.adobe.com/nexus/content/groups/public/</url>
   <layout>default</layout>
  </pluginrepository>
 </pluginrepositories>

 <build>
  <pluginmanagement>
   <plugins>
    
        
    <plugin>
     <groupid>org.apache.sling</groupid>
     <artifactid>maven-sling-plugin</artifactid>
     <version>2.1.0</version>
     <configuration>
      <username>${crx.username}</username>
      <password>${crx.password}</password>
     </configuration>
    </plugin>
         
    <plugin>
     <groupid>com.day.jcr.vault</groupid>
     <artifactid>content-package-maven-plugin</artifactid>
     <version>0.0.20</version>
     <extensions>true</extensions>
     <configuration>
      <failonerror>true</failonerror>
      <username>${crx.username}</username>
      <password>${crx.password}</password>
     </configuration>
    </plugin>
    <plugin>
     <groupid>org.eclipse.m2e</groupid>
     <artifactid>lifecycle-mapping</artifactid>
     <version>1.0.0</version>
     <configuration>
      
      
     </configuration>
    </plugin>
   </plugins>
  </pluginmanagement>
 </build>

 <profiles>
   
  <profile>
   <id>autoInstallBundle</id>
   <build>
    <plugins>
     <plugin>
      <groupid>org.apache.sling</groupid>
      <artifactid>maven-sling-plugin</artifactid>
      <executions>
       <execution>
        <id>install-bundle</id>
        <goals>
         <goal>install</goal>
        </goals>
       </execution>
      </executions>
     </plugin>
    </plugins>
   </build>
  </profile>
 </profiles>
 
 
 <modules>
  <module>bundle</module>
  <module>content</module>
 </modules>
</project>

2) pom.xml of bundle module:

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd ">
    <modelversion>4.0.0</modelversion>
    <parent>
        <groupid>com.surya.aem</groupid>
        <artifactid>blog-sample</artifactid>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactid>blog-sample-bundle</artifactid>
    <packaging>bundle</packaging>
    <name>Blog Sample Bundle</name>

    <dependencies>
  
    </dependencies> 

    <build>
        <plugins>
   
            <plugin>
                <groupid>org.apache.felix</groupid>
                <artifactid>maven-scr-plugin</artifactid>
                <executions>
                    <execution>
                        <id>generate-scr-descriptor</id>
                        <goals>
                            <goal>scr</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupid>org.apache.felix</groupid>
                <artifactid>maven-bundle-plugin</artifactid>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <bundle-symbolicname>com.surya.aem.blog-sample-bundle</bundle-symbolicname>
                    </instructions>
                </configuration>
            </plugin>
      
            <plugin>
                <groupid>org.apache.sling</groupid>
                <artifactid>maven-sling-plugin</artifactid>
                <configuration>
     
                    <slingurl>http://${crx.host}:${crx.port}/apps/blog/install</slingurl>
                    <useput>true</useput>
                </configuration>
            </plugin>
            
        </plugins>
    </build>
</project>

3) pom.xml of content project:
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelversion>4.0.0</modelversion>
    <parent>
        <groupid>com.surya.aem</groupid>
        <artifactid>blog-sample</artifactid>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactid>blog-sample-content</artifactid>
    <packaging>content-package</packaging>
    <name>Blog Sample Package</name>

    <dependencies>
        <dependency>
            <groupid>${project.groupId}</groupid>
            <artifactid>blog-sample-bundle</artifactid>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/content/jcr_root</directory>
                <filtering>false</filtering>
                <excludes>
                    <exclude>**/.vlt</exclude>
                    <exclude>**/.vltignore</exclude>
                </excludes>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupid>org.apache.maven.plugins</groupid>
                <artifactid>maven-resources-plugin</artifactid>
                <configuration>
                    <includeemptydirs>true</includeemptydirs>
                </configuration>
            </plugin>
  
            <plugin>
                <groupid>com.day.jcr.vault</groupid>
                <artifactid>content-package-maven-plugin</artifactid>
                <extensions>true</extensions>
                <configuration>
                    <group>Blog Sample</group>
                    <filtersource>src/main/content/META-INF/vault/filter.xml</filtersource>
                    <embeddeds>
      
                        <embedded>
                            <groupid>${project.groupId}</groupid>
                            <artifactid>blog-sample-bundle</artifactid>
                            <target>/apps/blog/install</target>
                        </embedded>
                    </embeddeds>
                    <targeturl>http://${crx.host}:${crx.port}/crx/packmgr/service.jsp</targeturl>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <profiles>
  

        <profile>
            <id>autoInstallPackage</id>
            <build>
                <plugins>
                    <plugin>
                        <groupid>com.day.jcr.vault</groupid>
                        <artifactid>content-package-maven-plugin</artifactid>
                        <executions>
                            <execution>
                                <id>install-content-package</id>
                                <phase>install</phase>
                                <goals>
                                    <goal>install</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    
        <profile>
            <id>autoInstallPackagePublish</id>
            <build>
                <plugins>
                    <plugin>
                        <groupid>com.day.jcr.vault</groupid>
                        <artifactid>content-package-maven-plugin</artifactid>
                        <executions>
                            <execution>
                                <id>install-content-package-publish</id>
                                <phase>install</phase>
                                <goals>
                                    <goal>install</goal>
                                </goals>
                                <configuration>
                                    <targeturl>http://${publish.crx.host}:${publish.crx.port}/crx/packmgr/service.jsp</targeturl>
                                    <username>${publish.crx.username}</username>
                                    <password>${publish.crx.password}</password>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

At this point you have a working AEM/CQ maven project in eclipse. With this maven project you can do lot of things like build automation, better unit testing and deployment management. One aspect that we have not covered so far is how to use this project for everyday development.

Let’s consider a very common use case. You have an AEM project for which you maintain your source code in SVN (or any other SCM) and you are using eclipse & CRXDELite for development. The reason I mentioned CRXDELite along with eclipse is that, there are certain tasks that are easy to perform with CRXDELite e.g. creating new component or template with a wizard. When you create anything in CRXDELite, you need a way to pull content created in CQ repository out in maven project (or file system) so that it can be source controlled in SVN so, how to do that? There are two ways to do this and both these ways uses “vlt” tool provided by AEM/CQ. Here are two ways:

  1. Eclipse Vault (vlt) plug-in OR, 
  2. Maven Vault plug-in
I’ll try to provide some information on how to use both these methods in my other blog posts as soon as possible.

Check out source code for this project: https://suryakand.googlecode.com/svn/aem/simple-aem-maven-project
Thank you for reading, if you have any question or find any error please feel free to leave a comment.

Comments

Anonymous said…
Hello - The multimodule-content-package-archetype does not seem to be valid. Execution of the suggested mvn command results in the error message - "desired archetype does not exist." Does this still work for you? I see it listed in the catalog so am not sure what the problem is.
I tried the sample-project-archetype and had success with it.
Anonymous said…
Hi,
Thanks for your post.
I read this at the end:
"Here are two ways:

Eclipse Vault (vlt) plug-in OR,
Maven Vault plug-in

I’ll try to provide some information on how to use both these methods in my other blog posts as soon as possible."
Just curious if you've done a post on either of the 2 methods, I'd be very interested.
Unknown said…
Hi Suryakand,

Very nice post. Thank you for sharing your experience developing for AEM in Eclipse, but as Adobe's documentation, it stops when it is time to develop components on Eclipse, hence I am on my own.

When creating a JSP, the very first thing to do is add this code: <%@include file="/libs/foundation/global.jsp"%> to import all libs, but it tries to find this folder into the content module and it not exists and everything has errors like this.

There is any SDK, jar or another thing that i can do to fix this reference issue?

I have tried to checkout the libs folder, adding it to the vault filter, but the project becomes to slow to handle inside eclipse.

Please, tell me more about your experience developing inside Eclipse.
svenan said…
There is one good approach is to have a global jsp specific for your application. So u would have /apps/project/global.jsp with the same content from the global one. That will go in the package and Eclipse will find it. You can have ten your apps defaults in this file too. E. G. Other taglibs load etc.
Surya
I am using AEM 6.0 version and trying to set up a development environment.Installed Eclipse,Maven,Vaultclipse but while seting the multimodule-content-package-archetype using the mvn command as mentioned in your article - it fails with an Error
"The goal you specified requires a project to execute but there is no POM
in this directory (C:\workspace). Please verify you invoked Maven from the correct directory."

Please Advise - andyC
admin said…
HI, and thanks for mr suryakand,

i works for me but in different way,

I just create a project in maven using this

mvn archetype:generate -DarchetypeRepository=http://repo.adobe.com/nexus/content/groups/public/ -DarchetypeGroupId=com.day.jcr.vault -DarchetypeArtifactId=multimodule-content-package-archetype -DarchetypeVersion=1.0.2 -DgroupId=com.aem.community -DartifactId=echoproject -Dversion=1.0-SNAPSHOT -Dpackage=com.aem.community -DappsFolderName=myproject -DartifactName="My Project" -DcqVersion="5.6.1" -DpackageGroup="My Company"

when is created you get into the folder where the xml is located and upload it to CRX/DE using this commands in command prompt

mvn -PautoInstallPackage install

i have to say that with the other commands i had ERRORS but using these commands everything works good.

thanks a lot from costa rica - CENTRAL AMERICA
Unknown said…
Hi,

Thank you for the great explanation above.

I am facing some issue in installing ews.ui.apps package to publish instance using mvn command.
I am using AEM 6.0

This was all working fine until a few days ago, when my instances were using a Mongodb, I have migrated to TarMK now on publish, and pckage install did work on it until yesterday.

The command used : [‎12/‎3/‎2015 11:41 AM] Mehta, Nidhi:
mvn -f ${WORKSPACE}/pom.xml -PautoInstallPackage install -DskipTests -Daem.host=${aempublishhost} -Daem.port=${aempublishport} -Dsling.user=${slingpublishuser} -Dsling.password=${slingpublishpassword} -Dvault.user=${vaultpublishuser} -Dvault.password=${vaultpublishpassword}

The variables are replaced by the corresponding env values

Error :
02.12.2015 01:00:21.977 *ERROR* [qtp1256224258-151] org.apache.jackrabbit.vault.fs.io.Importer Error while committing : javax.jcr.RepositoryException: This session has been closed.
02.12.2015 01:00:21.977 *WARN* [qtp1256224258-151] org.apache.jackrabbit.vault.fs.io.Importer Error while committing changes. Retrying import from checkpoint at /etc/designs/ews/images/icons/check.
png/jcr:content/renditions/original. Retries 0/10
02.12.2015 01:00:21.978 *ERROR* [qtp1256224258-151] org.apache.jackrabbit.vault.packaging.impl.ZipVaultPackage Error during install.
javax.jcr.RepositoryException: This session has been closed.


Now, I have not been able to find out whats causing the session to be closed. When I run the curl command to install package, it works fine. what different does mvn does that TarMK is closing the sessions?

Also, I have tried pointing it back to my old mongodb and it works great

Error logs also just show this error on publish , and this is an issue seen with only ews.ui.apps package.

I have recently migrated to Java8.

Please advice !!

Thanks
Unknown said…
Hi,

Thank you for the great explanation above.

I am facing some issue in installing ews.ui.apps package to publish instance using mvn command.
I am using AEM 6.0

This was all working fine until a few days ago, when my instances were using a Mongodb, I have migrated to TarMK now on publish, and pckage install did work on it until yesterday.

The command used : [‎12/‎3/‎2015 11:41 AM] Mehta, Nidhi:
mvn -f ${WORKSPACE}/pom.xml -PautoInstallPackage install -DskipTests -Daem.host=${aempublishhost} -Daem.port=${aempublishport} -Dsling.user=${slingpublishuser} -Dsling.password=${slingpublishpassword} -Dvault.user=${vaultpublishuser} -Dvault.password=${vaultpublishpassword}

The variables are replaced by the corresponding env values

Error :
02.12.2015 01:00:21.977 *ERROR* [qtp1256224258-151] org.apache.jackrabbit.vault.fs.io.Importer Error while committing : javax.jcr.RepositoryException: This session has been closed.
02.12.2015 01:00:21.977 *WARN* [qtp1256224258-151] org.apache.jackrabbit.vault.fs.io.Importer Error while committing changes. Retrying import from checkpoint at /etc/designs/ews/images/icons/check.
png/jcr:content/renditions/original. Retries 0/10
02.12.2015 01:00:21.978 *ERROR* [qtp1256224258-151] org.apache.jackrabbit.vault.packaging.impl.ZipVaultPackage Error during install.
javax.jcr.RepositoryException: This session has been closed.


Now, I have not been able to find out whats causing the session to be closed. When I run the curl command to install package, it works fine. what different does mvn does that TarMK is closing the sessions?

Also, I have tried pointing it back to my old mongodb and it works great

Error logs also just show this error on publish , and this is an issue seen with only ews.ui.apps package.

I have recently migrated to Java8.

Please advice !!

Thanks
Anonymous said…
Great post Suryakand!

One question I had: We've changed the version of the parent, bundle, and content POM files from "1.0-SNAPSHOT" to "${project.version}" and now the vault filters (located under \content\src\main\content\META-INF\vault) are not applied to the overall package.

Is there anything else that needs to be changed in order for the filters to appear again?

Thanks in advance!

Popular posts from this blog

AEM as a Cloud Service (AEMaaCS) – Architecture Overview

Custom synchronisation or rollout action for blueprint ?

AEM as a Cloud Service (AEMaaCS) – Quick Introduction

Generating URL based on AEM RUN mode using AEM Externalizer Service