<plugin>
<groupId>net.adamcin.oakpal</groupId>
<artifactId>oakpal-maven-plugin</artifactId>
<version>1.2.0</version>
<executions>
<execution>
<goals>
<goal>scan</goal>
</goals>
</execution>
</executions>
</plugin>
If you develop code for AEM, then you probably build and install content-packages from source using Maven. You might also be familiar with the fact that the process of installing such a package on AEM can be prone to both non-deterministic failures and less-than-complete “successes.”
What you may not know is that, other than the traditional culprits of lack of disk space or permissions, or the occasional Package Manager bundle restart, the vast majority of package installation failures are caused by a capricious gremlin that lives in the packages themselves, and that gremlin’s name is “DocView.”
The Insidiousness of DocView Errors
FileVault uses the JCR Document View (DocView) XML serialization format to import arbitrary trees of nodes from packages into the repository.
The DocView format consists of mapping XML element names to JCR node names, and XML attributes to JCR properties. The relationship between the two models is so easy to grasp that the small impedance mismatch that actually exists can lead to mistakes that are very difficult to detect in an IDE or during code review.
Some common mistakes are:
But watching for errors is a challenge as well. When individual DocView files fail to import, the errors raised are often buried in verbose log streams or are suppressed completely during development. Even more frustrating is that these errors do not often fail the package installation, and instead of the importer merely spits out an “E” for the path and then continues undaunted, returning a 200 response code while only dribbling out an underwhelming log message at the end:
saving approx 4832842 nodes...
Package imported (with errors, check logs!)
Attack the DocView Gremlin at the Source
To fill the AEM package accepting the testing gap, I’ve created a project called OakPAL – The Oak Package Acceptance Library that exposes a simple java API for simulating content package installation without incurring the overhead of launching an OSGi runtime or connecting to an HTTP port. Built around this core API, the oakpal-maven-plugin hooks into your maven lifecycle during the integration-test phase to identify any possible issues with the package itself that would prevent installation downstream.
The first step is to add the plugin to your content-package pom.
FileVault uses the JCR Document View (DocView) XML serialization format to import arbitrary trees of nodes from packages into the repository.
The DocView format consists of mapping XML element names to JCR node names, and XML attributes to JCR properties. The relationship between the two models is so easy to grasp that the small impedance mismatch that actually exists can lead to mistakes that are very difficult to detect in an IDE or during code review.
Some common mistakes are:
- Unescaped ampersands in attributes (very common when editing these files by hand)
- .content.xml files mangled by blind search-and-replace operations
- Use of undeclared or unregistered JCR namespace prefixes in attribute values
- Missing or violated node type definitions
- Unspecified parent node types, which default to the hilariously restrictive nt:folder
But watching for errors is a challenge as well. When individual DocView files fail to import, the errors raised are often buried in verbose log streams or are suppressed completely during development. Even more frustrating is that these errors do not often fail the package installation, and instead of the importer merely spits out an “E” for the path and then continues undaunted, returning a 200 response code while only dribbling out an underwhelming log message at the end:
saving approx 4832842 nodes...
Package imported (with errors, check logs!)
Attack the DocView Gremlin at the Source
To fill the AEM package accepting the testing gap, I’ve created a project called OakPAL – The Oak Package Acceptance Library that exposes a simple java API for simulating content package installation without incurring the overhead of launching an OSGi runtime or connecting to an HTTP port. Built around this core API, the oakpal-maven-plugin hooks into your maven lifecycle during the integration-test phase to identify any possible issues with the package itself that would prevent installation downstream.
The first step is to add the plugin to your content-package pom.
<plugin>
<groupId>net.adamcin.oakpal</groupId>
<artifactId>oakpal-maven-plugin</artifactId>
<version>1.2.0</version>
<executions>
<execution>
<goals>
<goal>scan</goal>
</goals>
</execution>
</executions>
</plugin>
At this point, your module will attempt to install the package artifact into a vanilla Oak repository–the keyword being “vanilla”.
A happy installation looks like this:
[INFO] --- oakpal-maven-plugin:1.2.0:scan (default) @ my-project.ui.apps ---
[INFO] Found a new index node [reference]. Reindexing is requested
[INFO] Reindexing will be performed for following indexes: [/oak:index/uuid, /oak:index/reference, /oak:index/nodetype]
[INFO] Indexing report
- /oak:index/nodetype*(1257)
[INFO] Reindexing will be performed for following indexes: [/oak:index/principalName, /oak:index/authorizableId, /oak:index/acPrincipalName, /oak:index/repMembers]
[INFO] Indexing report
- /oak:index/principalName*(2)
- /oak:index/authorizableId*(2)
If all you have are folders, jar files, and nt:unstructured nodes, no problem. But more likely than not, you are developing a package containing Sling resources, or AEM templates and components, which means you are probably dependent on the namespaces and nodetypes that are installed only with the product, like sling:OsgiConfig and cq:Component, for example.
Without these nodetypes you will probably see FileVault logging similar to this:
[ERROR] Error during processing of /apps/my-project/components/content/colctrl: javax.jcr.nodetype.NoSuchNodeTypeException: Node type cq:Component does not exist
Followed by associated OakPAL Violation Reports:
[ERROR] E /apps/my-project/components/content/colctrl (javax.jcr.nodetype.NoSuchNodeTypeException: Node type cq:Component does not exist)
[ERROR] E /apps/my-project/components/content/colctrl/clientlib (java.lang.IllegalStateException: Parent node not found.)
[ERROR] E /apps/my-project/components/content/colctrl/clientlib/css.txt (java.lang.IllegalStateException: Parent node not found.)
[ERROR] E /apps/my-project/components/content/colctrl/clientlib/style.css (java.lang.IllegalStateException: Parent node not found.)
Followed by associated OakPAL Violation Reports:
[INFO] OakPAL Reporter: jar:file:/Users/madamcin/.m2/repository/net/adamcin/oakpal/oakpal-core/1.2.0/oakpal-core-1.2.0.jar!/net/adamcin/oakpal/core/DefaultErrorListener.class
[ERROR] +- <MAJOR> /apps/my-project/components/content/colctrl - Importer error: javax.jcr.nodetype.NoSuchNodeTypeException "Node type cq:Component does not exist"
[ERROR] +- <MAJOR> /apps/my-project/components/content/colctrl/clientlib - Importer error: java.lang.IllegalStateException "Parent node not found."
[ERROR] +- <MAJOR> /apps/my-project/components/content/colctrl/clientlib/css.txt - Importer error: java.lang.IllegalStateException "Parent node not found."
[ERROR] +- <MAJOR> /apps/my-project/components/content/colctrl/clientlib/style.css - Importer error: java.lang.IllegalStateException "Parent node not found."
To install the AEM platform node types, you can export them from CRX/de lite.
Export your AEM platform nodetypes
To properly prepare the scan for your code package, you might first need to export the Compact NodeType Definition (CND) from your installed version of AEM and make it available to the plugin.
For a developer, it is as simple as visiting crx/de lite on a representative installation, such as a properly patched local quickstart server.
Navigate in the toolbar to Tools > Export Node Type:
You will see the generated CND content rendered directly.<'sling'='http://sling.apache.org/jcr/sling/1.0'> <'nt'='http://www.jcp.org/jcr/nt/1.0'> <'cq'='http://www.day.com/jcr/cq/1.0'> <'oak'='http://jackrabbit.apache.org/oak/ns/1.0'> <'jcr'='http://www.jcp.org/jcr/1.0'> <'mix'='http://www.jcp.org/jcr/mix/1.0'> <'granite'='http://www.adobe.com/jcr/granite/1.0'> <'rep'='internal'> <'xmp'='http://ns.adobe.com/xap/1.0/'> <'social'='http://www.adobe.com/social/1.0'> <'dam'='http://www.day.com/dam/1.0'> <'oauth'='http://oauth.net/'> <'rdf'='http://www.w3.org/1999/02/22-rdf-syntax-ns#'> <'vlt'='http://www.day.com/jcr/vault/1.0'> <'slingevent'='http://sling.apache.org/jcr/event/1.0'> <'fd'='http://www.adobe.com/aemfd/fd/1.0'>
[sling:OrderedFolder] > sling:Folder
orderable
+ * (nt:base) = sling:OrderedFolder version
[cq:OwnerTaggable] > cq:Taggable
mixin
[oak:Unstructured]
- * (undefined) multiple
- * (undefined)
+ * (nt:base) = oak:Unstructured version
...
Save the output as a file under src/test/resources in your ui.apps module and add the <cndNames>/<cndName> the parameter to your oakpal-maven-plugin configuration with the path to the file relative to src/test/resources.
Save the output as a file under src/test/resources in your ui.apps module and add the <cndNames>/<cndName> the parameter to your oakpal-maven-plugin configuration with the path to the file relative to src/test/resources.
<plugin>
<groupId>net.adamcin.oakpal</groupId>
<artifactId>oakpal-maven-plugin</artifactId>
<version>1.2.0</version>
<configuration>
<cndNames>
<cndName>[your-cnd-filename]</cndName>
</cndNames>
</configuration>
<executions>
<execution>
<goals>
<goal>scan</goal>
</goals>
</execution>
</executions>
</plugin>
Run mvn install again and hope for success
Advanced Case Study: Dependency on ACS AEM Commons
Things are never as simple as they seem in the AEM world, and this plugin is proud to follow in that tradition. You may have already started asking questions like, “What if my package depends on another package being installed first?”, and “What if the exported CND doesn’t install all the namespaces that my package depends on?”. I’ll answer those questions by demonstrating how to handle the common situation where your code package has a dependency on ACS AEM Commons.
To successfully simulate installation into a repository where ACS Commons has been installed we will need to:
Things are never as simple as they seem in the AEM world, and this plugin is proud to follow in that tradition. You may have already started asking questions like, “What if my package depends on another package being installed first?”, and “What if the exported CND doesn’t install all the namespaces that my package depends on?”. I’ll answer those questions by demonstrating how to handle the common situation where your code package has a dependency on ACS AEM Commons.
To successfully simulate installation into a repository where ACS Commons has been installed we will need to:
- Register the crx namespace
- Register the crx:replicate privilege
- Pre-install the acs-aem-commons-content package
<plugin>
<groupId>net.adamcin.oakpal</groupId>
<artifactId>oakpal-maven-plugin</artifactId>
<version>1.2.0</version>
<configuration>
<cndNames>
<cndName>[your-cnd-filename]</cndName>
</cndNames>
<jcrNamespaces>
<jcrNamespace>
<prefix>crx</prefix>
<uri>http://www.day.com/crx/1.0</uri>
</jcrNamespace>
</jcrNamespaces>
<jcrPrivileges>
<jcrPrivilege>crx:replicate</jcrPrivilege>
</jcrPrivileges>
<preInstallArtifacts>
<preInstallArtifact>
<groupId>com.adobe.acs</groupId>
<artifactId>acs-aem-commons-content</artifactId>
<version>4.0.0</version>
<type>zip</type>
</preInstallArtifact>
</preInstallArtifacts>
</configuration>
<executions>
<execution>
<goals>
<goal>scan</goal>
</goals>
</execution>
</executions>
</plugin>
I encourage you to read the oakpal-maven-plugin:scan reference page to see the other available options.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.