Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

Sling Models: From Scriptlets to POJOs

Posted on March 7, 2014 By Luis Fernandez
Description: Sling Models: From Scriptlets to POJOs: Apache Sling from a practitioner’s perspective with timeless lessons.

A quick chat in the team room

Me: Why is there Java in this JSP that looks like spaghetti?
Teammate: It started small. A getter here, a null check there. Then a deadline hit. You know the rest.
Me: What if we move the logic into a POJO, adapt it from the Resource, and keep the template clean?
Teammate: Like a model per component?
Me: Exactly. Apache Sling Models. Annotations. Injection. No custom base class. And unit tests that do not need a running container.
Teammate: Sold. Show me.

Evidence that this helps

Here is what we see when we pull logic out of JSP scriptlets and into Sling Models:
  • Separation of concerns: JSP renders. The model prepares data. Review diffs without hunting through tags and scriptlets.
  • Less repetition: Normalize dates, images, and links once in the model, reuse across components, and stop copying checks into every JSP.
  • Testability: Create the model as a plain object and verify behavior. No container, no mocks for request binding unless you need them.
  • Safer rendering: The template only reads values. Your null and fallback logic lives in one place. Fewer surprises in production.
  • Performance that makes sense: Model instances are light and short lived. Adapt from the Resource or Request and move on. You can also compute once and cache per request when needed.
A small before and after to make it real.
Old approach in a JSP, mixing concerns:
<%@ page import="org.apache.commons.lang3.StringUtils" %>
<% 
  String title = currentPage.getProperties().get("jcr:title", "");
  if (StringUtils.isBlank(title)) {
    title = currentPage.getName();
  }
  String byline = properties.get("byline", "Staff");
%>
<h2><%= xssAPI.encodeForHTML(title) %></h2>
<p class="by"><%= xssAPI.encodeForHTML(byline) %></p>
New approach with a Model that keeps the JSP clean:
package com.example.site.models;

import javax.annotation.PostConstruct;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.Inject;
import org.apache.sling.models.annotations.injectorspecific.Named;

@Model(
    adaptables = Resource.class,
    defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class Article {

  @Inject @Named("jcr:title")
  private String title;

  @Inject
  private String byline;

  @Inject
  private Resource resource;

  private String safeTitle;
  private String safeByline;

  @PostConstruct
  protected void init() {
    safeTitle = isBlank(title) ? resource.getName() : title;
    safeByline = isBlank(byline) ? "Staff" : byline;
  }

  public String getTitle() { return safeTitle; }
  public String getByline() { return safeByline; }

  private boolean isBlank(String s) { return s == null || s.trim().isEmpty(); }
}
And the JSP gets very small:
<%@ page import="com.example.site.models.Article" %>
<% Article m = resource.adaptTo(Article.class); %>
<h2><%= xssAPI.encodeForHTML(m.getTitle()) %></h2>
<p class="by"><%= xssAPI.encodeForHTML(m.getByline()) %></p>

Build notes for Sling Models in your project

You only need the API and the model engine. Bring them into your OSGi bundle and you are ready.
<dependency>
  <groupId>org.apache.sling</groupId>
  <artifactId>org.apache.sling.models.api</artifactId>
  <version>1.0.0</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.apache.sling</groupId>
  <artifactId>org.apache.sling.models.impl</artifactId>
  <version>1.0.0</version>
</dependency>
A few bite sized tips:
  • Choose the right adaptable: If you only need content, adapt from Resource. If you need selectors or suffix or user info, adapt from SlingHttpServletRequest.
  • Inject what you need: Use @Inject for properties and services. For a property name that does not match your field, use @Named("jcr:title").
  • Default safely: Compute fallbacks in @PostConstruct so getters are simple. Keep templates dumb.
  • Only escape in the view: Keep raw values in the model. Let JSP handle XSS escaping with xssAPI.
  • Keep models small: A model per component or per concern. No god classes.
Example with request adaptable and a service:
package com.example.site.models;

import javax.annotation.PostConstruct;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.Inject;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;

@Model(adaptables = SlingHttpServletRequest.class)
public class Hero {

  @Inject
  private String title;

  @OSGiService
  private LinkResolver linkResolver;

  @Inject
  private SlingHttpServletRequest request;

  private String link;

  @PostConstruct
  void init() {
    ResourceResolver rr = request.getResourceResolver();
    link = linkResolver.resolve(rr, request.getRequestPathInfo().getSuffix());
  }

  public String getTitle() { return title; }
  public String getLink() { return link; }
}

Risks and gotchas

  • Version alignment: On some stacks you might not have models on day one. Bring the API and the engine bundle, and confirm they start cleanly with no package conflicts.
  • Null injection: Missing content becomes null. Mark optional fields or set defaults in @PostConstruct to avoid surprises.
  • Heavy work in getters: Do the work once in @PostConstruct. Getters should be cheap and boring.
  • Over injection: Models are not service bags. Inject what you need. Pass the rest in via the adaptable.
  • Thread safety: Each model instance is short lived. Do not store static state. Do not cache across requests unless you know the life cycle.
  • Rendering safety: Escape at the point of output. The model should not return HTML mixed with content unless you control it fully.
  • Adapting from the wrong thing: If injection fails, check the adaptable. A field that needs request context will not resolve when you adapt from resource.

A simple way to wrap this up

Start small. Pick one component that always collects little utilities in its JSP. Create a Sling Model named after it. Move the data shaping code into the model, add @Inject for the properties you use, and write a couple of unit tests for the edge cases you know users hit. Then switch the JSP to adapt the model and read getters. Ship it. Watch your diffs get smaller and your reviews get friendlier.
This is not just cleaner code. It is a way to keep Apache Sling doing what it does best: adapt from content and request to a plain object that fits your component. From there, testing and maintenance get easier, and your pages stop carrying hidden logic in template land.
If your team is juggling new builds and lots of front end work, this shift buys you clarity without a big rewrite. You can migrate component by component, keep the same markup, and keep moving. When you look back after a few sprints, you will notice fewer bugs caused by last minute scriptlet edits and more confidence touching old components.
If you are working in AEM or plain Sling, or you are just curious about better patterns around JSP and OSGi, give Sling Models a try on the next ticket. Your future self will send you a thank you note.
Software Engineering Technical Implementation

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes