2016-08-02

Tags: JSP, AEM

TL/DR;

  • prefer Resource over Node

  • prefer ValueMap over PropertyIterator / Property

  • Encapsulate business logic with Apache Sling Models for a component (even works on component’s children!)

  • Use API (especially extending WCMUse or WCMUsePojo) built to support Sightly, but there is no reason they can’t be used in JSPs.

I remember attending training for AEM® back in the CQ5.3-5.4 days. The material spent much of its time going over the mechanics of a hierarchical "NoSQL" data store, as well as intricacies of OSGi that it had little time left for best practices. We would expend so much effort around learning the JCR, Node and Property that we could barely notice all the typed Exceptions those APIs threw. Unfortunately, the training (at least then) didn’t include content around core JSP features such as Tag Libraries or Expression Language. The primary APIs had some also rough spots that didn’t always lend themselves to leveraging these features. The result was the example code/components often had anti-patterns such as over-leveraging scriptlets (code inside the "template" of a JSP), which meant much of our initial code was also ugly. Ugly code has a cost for understanding and maintenance.

Over time, the situation has evolved and improved. By recognizing some of the warts, AEM® decided to create new languages & approaches that wouldn’t have the same problems. At the same time, updates to enable cleaner approaches benefit the old languages.

Avoiding Complexity

(especially error handling and exceptions)

The foundational APIs for javax.jcr.* are designed accessing an heirarchical "NoSQL" data store. They certainly are not designed for templating or business logic. javax.jcr.\* encapsulates nodes, sessions and permissions while exposing how any of those can go wrong (just look at the 21 subclasses of RepositoryException). Unfortunately, early training materials and examples (such as /apps/geometrixx/components/asseteditor/head.jsp) show how unsuited these APIs are for JSP development.

Fortunately, Sling APIs came along to improve the state of development. org.apache.sling.api.resource.* is a much friendlier API. To start with, Exceptions are rare to non-existant. Instead of throwing errors, they frequently provide fallback mechanisms to express the business intent when the JCR API would require error or exception handling.

Compare accessing a property value with Sling APIs (in raw Java):

    String getPageTitle(@Nonnull Resource resource) {
        ValueMap properties = resource.getValueMap();
        return properties.get("pageTitle", properties.get("jcr:title", properties.get("navTitle", "")));
    }

is much easier to maintain than JCR APIs:

    String getPageTitle(@Nonnull Node currentNode) {
        try {
            Property titleProp =
                currentNode.hasProperty("pageTitle") ? currentNode.getProperty("pageTitle") :
                    currentNode.hasProperty("jcr:title") ? currentNode.getProperty("jcr:title") :
                        currentNode.hasProperty("navTitle") ? currentNode.getProperty("navTitle") : null;
            if (titleProp != null) {
                return titleProp.getString();
            }
        } catch (RepositoryException ignore) {
            // log problem so you can find in monitoring & hopefully fix (author) soon.
        } finally {
            return ""; // better than returning null and risking NPE. Web is about concatenating strings, anyway! :-)
        }
    }

Focusing on the Intent

"Boilerplate" is the term used for all the code that is for the compiler, not the developer. Functional Programming is the paradigm of declaring what you want instead of how to accomplish a task (compared to Imperative Programming). The benefit of describing what instead of how is clearer intent often with less code. Wouldn’t it be nice to encapsulate a component with a POJO instead of describing how to read the JCR and then transform into objects? Then our views or templates could leverage a simple POJO with clean data instead of accessing the data, verifying & cleaning the data, and then presenting the result.

AdapterFactory was the initial attempt to structure and separate concerns. It architected your component classes to construct your objects from a Resource/ValueMap/SlingHttpServletRequest (or other "adaptables"). As repeated patterns of component classes took form, it became obvious (and desireable) to declare classes as injection containers automatically wired from declared properties (relative to JCR node properties). Thus, Sling Models was born. Instead of imperative code describing how to construct an object, developers can use annotations to declare the mapping to the JCR.

A good comparison of examples of classes is found in the Experience Delivers blog. Their patterns are:

  1. Use API

    1. Implement the Use interface

    2. Extend the WCMUsePojo class

  2. AdapterFactory

    1. from Resource

    2. from Request

  3. Sling Models

Conclusion

I appreciate the newer patterns for building classes. But I believe AEM® is disingenious when they sell Sightly as faster than JSP development when Java classes must be built and deployed as bundles before the new HTML templates can be built. For one thing, JSPs can leverage the exact same classes that are used to power Sightly. It is also possible to rapid prototype by extracting classes in JSPs and eventually move them to external bundles (with unit tests, of course!)

In the next part of this series, I hope to show how JSPs can look much closer to a template.