Creating and Configuring your Maven Project for AEM

Creating and Configuring your Maven Project for AEM

The goal of this document is to have a maven project that builds a single asset (the package profile) to deploy all Servlets, Services, and JCR Code. In addition to this, for development purposes we want to be able to build each item listed above individually, such that we do not require a full build to test a simple JSP change.

For starters, this is a fairly common project structure that we'll be basing this document off of:

<master>
      |______vault
      |______services
      |______servlets
      |______package

As mentioned above, our goal is to have the vault package contain all JCR code (CSS,JS,JSP, etc), services to include OSGi Services, servlets for OSGi Servlets, and package to be the "all-in-one" deploy.

The first problem I found with many implementations was that building the master project would also build and install each child module. This obviously is not desired, but given the "install" goal is being sent from the master, it obviously would inherrit to each child! Given that the "package" sub-module installs all of the other sub-modules, this would create a double-install, which obviously goes against any best practice. This being said, I have seen many production deployments where they do not use the "all-in-one""" package technique for deployments, in which this situation would be fine. In order to configure your module to build and not install, assuming you are using our own 6d plugin, your <module>/pom.xml would contain the following:

<plugin>
    <groupId>com.sixdimensions.wcm.cq</groupId>
    <artifactId>cq-deploy-plugin</artifactId>
    <version>0.1.8</version>
</plugin>

Notice there is no configuration items set for CQ user/port or the URL itself. In addition the "<executions>" tag has been removed. This is intended! Now the child module is just going to build and not install.

In order to get the child modules to allow for individual install, I created the following profiles in the parent pom:

  1. One profile for each child module:
    • services,
    • vault,
    • servlets
  2. as well as another profile for the "wrapper" profile: package (more on this later)

In each of the profile definitions, I specify JUST their own module, with the exception of the "wrapper", which, again, I'll explain later. This ends up with you having something similar to the following for each module configured in your parent pom:

<profile>
    <id>services</id>
    <activation>
        <activeByDefault>false</activeByDefault>
    </activation>
    <modules>
        <module>services</module>
    </modules>
</profile>

The above is saying, when you call mvn clean install -P services, just the services module will be run. Now since we configured the services module to JUST build, this wouldn't allow us to do a proper individual component deploy. This is where the inheritance comes in handy for MVN. What we can do here is add a profile for the child POM as well (this can actually be done in the parent pom as well, but it would result in a much larger file...). The profile would look like the following:

<profile>
    <id>services</id>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>com.sixdimensions.wcm.cq</groupId>
                    <artifactId>cq-deploy-plugin</artifactId>
                    <version>0.1.8</version>
                    <executions><execution><goals><goal>install-bundle</goal></goals></execution></executions>
                    <configuration>
                        <host>${cq.host}</host>
                        <port>${cq.port}</port>
                        <user>${cq.user}</user>
                        <password>${cq.password}</password>
                        <path>${project.build.bundleDir}</path>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</profile>

Now due to inheritance from the parent pom, if you invoked a mvn clean install -P services, the "services" profile above would be triggered in addition to the "services" profile configured in the parent pom. In the services pom, you notice I added the pluginManagement section to configure the 6d deploy plugin. What you see here is similar to what the plugin would look if you had it configured to install immediately after building. PluginManagement is mavens easy way to configure plugins through inheritance without needing to re-write the build order, which is why we used that instead of the direct plugins tag. You will notice I am using some properties I set in the master pom as well. These are pretty commonly passed into the maven build as -D<prop>=<value> pairs (i.e -Dcq.user=admin) then set as a default to your local instance in the master.

So at this point we have a build that, from the parent pom, we can install individual modules directly to CQ. Now we need to get the "wrapper" module to build (not install) all child modules, copy the built assets, and then build and install the cq package. For this we would configure the parent pom with the following profile (ours is set to install package by default, you can force them to pass in a profile if needed):

<profile>
    <id>default</id>
    <activation>
        <activeByDefault>true</activeByDefault>
    </activation>
    <modules>
        <module>services</module>
        <module>servlets</module>
        <module>package</module>
    </modules>
</profile>

The above tells maven to run mvn clean install on each module (thus building the modules). Vault was omitted, since it is not compiled and instead just packaged up (we copy it into the package manually). Since when we invoked the parent pom we did not specify the "services" profile, the install configurations in that module would not be enabled! Lastly, in this wrapper module we will leave the install-package execution enabled so it WILL install to the server by default. At this point, the maven job will create the jar asset in the standard "target" folder of that module. It doesn't move the assets somewhere useful, which leaves us one additional step to run to copy the built assets into the module itself. This can be done using the resources plugin:

<resources>
    <!-- Include the META-INF/Vault filter -->
    <resource>
        <directory>${basedir}/../vault/META-INF</directory>
        <targetPath>META-INF</targetPath>
        <includes>
            <include>**/*.xml</include>
            <include>**/*.png</include>
        </includes>
    </resource>

    <!-- Include the Vault Files -->
    <resource>
        <directory>../vault/jcr_root</directory>
        <excludes>
            <exclude>**/.vlt</exclude>
            <exclude>**/.vltignore</exclude>
            <exclude>**/.DS_Store</exclude>
        </excludes>
        <filtering>false</filtering>
        <targetPath>jcr_root</targetPath>
    </resource>

    <!-- Include Services jar -->
    <resource>
        <directory>../services/target</directory>
        <includes>
            <include>*.jar</include>
        </includes>
        <filtering>false</filtering>
        <targetPath>jcr_root${project.build.bundleDir}</targetPath>
    </resource>
</resources>

So the above resources configurations copies the vault files and configuration from the "vault" module, and copies the compiled jars from the services and servlets modules. I use a master-level config property called ${project.build.bundleDir} to configure where in the JCR we want to store the assets (for example /apps/project/install ) and use that throughout the child modules for consistency. Once we have copied those items, we can configure the 6d-deploy plugin to wrap up the content and install it into cq. This would be the plugin config in the wrapper profile:

           
<plugin>
    <groupId>com.sixdimensions.wcm.cq</groupId>
    <artifactId>cq-deploy-plugin</artifactId>
    <version>0.1.8</version>
    <executions>
        <execution>
            <id>install-package</id>
            <goals>
                <goal>install-package</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <host>${cq.host}</host>
        <port>${cq.port}</port>
        <user>${cq.user}</user>
        <password>${cq.password}</password>
    </configuration>
</plugin>

Share this post

0 Comments

comments powered by Disqus