Sling Model Delegation Pattern with Lombok

AEM Core Components are a standard set components to be used with AEM. Built with Adobe's best practices and standards, Core Components provide a baseline set of functionality for any Sites implementation. However, it's often necessary to customize the functionality of core components to address project-specific requirements. The business logic for the core components is implemented in Sling Models, which can be customized using the delegation pattern.
For simplicity, let's customize the Button component by introducing two dialog fields: text color and background color. Following Adobe's best practices, we'll create a new proxy component to use the core component. For example, create the component in /apps/aem-demo/components/button. This structure is required for the delegation pattern and keeps the existing behavior intact.
button / .content.xml
<?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primaryType="cq:Component" jcr:title="Link &amp; Button" sling:resourceSuperType="core/wcm/components/button/v2/button" componentGroup="AEM Demo - Content"/>
For the sake of brevity, let's not show how to add two new dialog fields. Now, let's create a new Sling Model that extends the Core Button component. The Sling Model below uses the Delegation Pattern with existing behavior and fetches two new properties. However, this approach requires to implement all the methods of the interface.
ButtonLink.java
@Model(adaptables = SlingHttpServletRequest.class, adapters = Button.class, resourceType = "aem-demo/components/button") public class ButtonLink implements Button { @ValueMapValue private String textColor; @ValueMapValue private String backgroundColor; @Self @Via(type = ResourceSuperType.class) private Button coreButton; @Override public String getText() { return coreButton.getText(); } @Override public Link getButtonLink() { return coreButton.getButtonLink(); } // ... rest of the methods implementation ... public String getTextColor() { return textColor; } public String getBackgroundColor() { return backgroundColor; } }
As you can understand, this approach simplifies extending the core component and adding new functionality. You might be concerned about the requirement to implement all interface methods. In such cases, you can use Lombok to simplify the code.
@Delegate annotation is used to delegate the methods to the core component. It automatically generates implementations for ALL public methods from the Button interface and eliminates the need to manually write boilerplate delegation code.
ButtonLink.java
@Model(adaptables = SlingHttpServletRequest.class, adapters = Button.class, resourceType = "aem-demo/components/button") public class ButtonLink implements Button { @ValueMapValue private String textColor; @ValueMapValue private String backgroundColor; @Delegate @Self @Via(type = ResourceSuperType.class) private Button coreButton; public String getTextColor() { return textColor; } public String getBackgroundColor() { return backgroundColor; } }
However, there are scenarios where you may want to exclude certain methods from the delegation. For example, to exclude the getButtonLink method, you can use the @Delegate(excludes = DelegationExclusion.class) annotation. Then specify the method signatures in the DelegationExclusion interface and provide your own implementation for those methods.
ButtonLink.java
@Model(adaptables = SlingHttpServletRequest.class, adapters = Button.class, resourceType = "aem-demo/components/button") public class ButtonLink implements Button { @ValueMapValue private String textColor; @ValueMapValue private String backgroundColor; @Delegate(excludes = DelegationExclusion.class) @Self @Via(type = ResourceSuperType.class) private Button coreButton; @Override public Link getButtonLink() { // ... custom implementation ... return coreButton.getButtonLink(); } public String getTextColor() { return textColor; } public String getBackgroundColor() { return backgroundColor; } private interface DelegationExclusion { String getButtonLink(); } }
The delegation pattern with Lombok significantly simplifies customizing AEM Core Components. By using @Delegate, you can eliminate boilerplate code and focus on your custom functionality while preserving the existing behavior. This approach maintains Adobe's best practices and ensures your customizations remain clean and maintainable for enterprise AEM implementations.

References

Write your Comment