Sightly and Slice - where a beautiful markup meets a beautiful code

08 July 2014
Maciej Majchrzak
Frink_Cognifide_2016_HeaderImages_0117

Sightly – the new templating engine in AEM

AEM 6 brings a lot of new features. Among the great improvements in the technology stack (like new OAK repository) it also introduces a new way of component development. The new templating engine called Sightly is html-based and addresses one of the most common problems in AEM development – lack of separation of concerns. From now on, Sightly prevents you from writing the business logic of a component in the presentation layer – instead it offers a pure view and moves the business logic to Java classes. Thanks to this you can finally forget about JSP scriptlets – these ugly creatures can finally pass away…

There’s a number of great tutorials about Sightly, if you are not familiar with this technology you can take a look at Adobe’s tutorial.

My first Sightly component

Scanning the Sightly documentation and the tutorial prepared by Adobe, I decided to give it a try and create my very first component. My first impression was rather positive – especially when it came to simple components without more advanced Sightly features. I must admit the presentation layer was quite clear. Just take a look at it:

${model.text}

No business-logic code, simple and readable.
Now with my presentation layer ready, I started to write the business logic. I had four options to choose from:
•    Implementing Use interface
•    Extending WCMUse class
•    Adapting from resource
•    Adapting from request

Since I don’t like writing too much code, I chose extending the WCMUse class as it gives me a bit more than simple Use interface. I end up with code like this:

public class ArticleModel extends WCMUse {

	private List<LinkModel> links;

	@Override
	public void activate() throws Exception {
		Resource childResource = getResource().getChild("links");
		links = new ArrayList<Link>();
		if (childResource != null) {
			Iterator<Resource> children = childResource.listChildren();
			while (children.hasNext()) {
				Resource linkResource = children.next();
				LinkModel link = new LinkModel(linkResource);
				links.add(link);
			}
		}
	}

	public String getText() {
		return getProperties().get("text", String.class);
	}

	public List<LinkModel> getLinks() {
		return links;
	}
}

public class LinkModel {

	private String path;
	private String title;

	public LinkModel(Resource linkResource) {
		ValueMap properties = linkResource.adaptTo(ValueMap.class);
		path = properties.get("path", String.class);
		title = properties.get("title", String.class);
	}

	public String getPath() {
		return path;
	}

	public String getTitle() {
		return title;
	}
}

What do you think? Let’s analyse it:
•    The ArticleModel has two public methods which are used by the corresponding Sightly template (article.html)
•    To get text, I simply needed to use properties and read appropriate one
•    To get a list of links I needed to do some operations on resources which are not very complex but requires some code. I did it in activate method to read it once and save it in the object.

My conclusion? Creating a template was an easy job. Java code was also easy to write but could have been a bit simpler.

More complex logic, but how to test it?

So far, my simple article component can only read data from repository and display it. It’s basically free of any business logic. So, let’s create some. I added a title – if it’s not set by authors, it should default to current page title.

public String getTitle() {
	String title = getProperties().get("title", String.class);
	return StringUtils.defaultIfEmpty(title, getPageTitle());
}

private String getPageTitle() {
	Page currentPage = getCurrentPage();
	return StringUtils.defaultIfEmpty(currentPage.getTitle(), currentPage.getName());
}

I implemented two methods: the public one is used by the Sightly template and it reads a title from the resource properties or the default is to page title. Page title is read from the current page using the getCurrentPage method which is available thanks to extending the WCMUse class. It works. Do I like it though? To be honest, I can see a couple of problems here:
•    In the getPageTitle method, I’m using currentPage and then it picks either its title (if it’s set) or name. This logic should probably be extracted to a separate class as it might be quite commonly used in whole system. So a PageModel class would be helpful here.
•    My class cannot be unit-tested, especially the getTitle method. Why? To test the defaulting mechanism I would need to mock reading the title from properties and then the title from the current page. However, I cannot mock a class which I’m testing. The conclusion here is simple: if you are extending WCMUse class or implementing Use interface, your class won't be easily testable.
Since I’d like to test my classes which contain business logic I will need to find another solution. Luckily, Sightly allows me to create an adapter factory instead of using the WCMUse. I tried this and this is what I end up with:

public class ArticleModel {

	private List<Link> links;

	private String text;

	private String articleTitle;

	private PageModel pageModel;

	public ArticleModel(List<Link> links, String text, String articleTitle, PageModel pageModel) {
		this.links = links;
		this.text = text;
		this.articleTitle = articleTitle;
		this.pageModel = pageModel;
	}

	public String getTitle() {
		return StringUtils.defaultIfEmpty(articleTitle, getPageTitle());
	}

	private String getPageTitle() {
		return pageModel.getTitle();
	}

	public String getText() {
		return text;
	}

	public List<Link> getLinks() {
		return links;
	}
}

Nice! This is something I can write a unit test for! But…I also needed to write an adapter factory which looks like this:

@Component(metatype = true, immediate = true)
@Service
public class ArticleAdapterFactory implements AdapterFactory {

	@Property(name = "adapters")
	protected static final String[] ADAPTER_CLASSES = { ArticleModel.class.getName() };

	@Property(name = "adaptables")
	protected static final String[] ADAPTABLE_CLASSES = { Resource.class.getName() };

	@Override
	public <ArticleModel> ArticleModel getAdapter(Object adaptable, Class<ArticleModel> type) {
		if (adaptable instanceof Resource) {
			Resource resource = (Resource) adaptable;
			ValueMap properties = resource.adaptTo(ValueMap.class);
			String text = properties.get("text", String.class);
			String articleTitle = properties.get("articleTitle", String.class);
			PageModel pageModel = new PageModel(resource);
			List<Link> links = getLinks(resource);
			return new ArticleModel(links, text, articleTitle, pageModel);
		}
		return null;
	}

	public List<Link> getLinks(Resource resource) {
		List<Link> links = new ArrayList<Link>();
		Resource childResource = resource.getChild("links");
		if (childResource != null) {
			Iterator<Resource> children = childResource.listChildren();
			while (children.hasNext()) {
				Resource linkResource = children.next();
				LinkModel link = new LinkModel(linkResource);
				links.add(link);
			}
		}
		return links;
	}
}

As you can see, there’s quite a lot of code to instantiate my object and an additional class (factory) needs to be implemented. The code is not very readable and I bet my adapters will shortly become a pain point in the application – something which will be boring to write and hard to maintain.

Slice your code to make it simpler

I got used to using Slice framework in my previous applications – a framework which made CQ development simpler and quicker (check my blog post for details about Slice framework). Using pure Sightly wasn’t a big amusement – I basically needed to do a small step back in my development and write everything from scratch. Therefore, I decided to marry Sightly and Slice to find out what benefits this would give me.

I started with the newest version of the Slice framework – version 4.0 which gives support for Sightly and a couple of nice new features – you can read about it on Slice wiki.
The code for my component finally became simple, and fully testable. I was pleased of this:

@SliceResource
public class ArticleModel {
	@JcrProperty
	private String articleTitle;

	@JcrProperty
	private String text;

	@JcrProperty
	@Children(LinkModel.class)
	private <Link> links;	

	private PageModel pageModel;

	@Inject
	public ArticleModel(@CurrentPage PageModel pageModel) {
		this.pageModel = pageModel;
	}

	public String getTitle() {
		return StringUtils.defaultIfEmpty(articleTitle, getPageTitle());
	}

	private String getPageTitle() {
		return pageModel.getTitle();
	}
}

Yep – that’s everything! No implementation of Use interface, no WCMUse class and no adapter factory to be implemented. It's as simple as that!

You probably paid attention to the constructor of my new model. First, it is annotated by @Inject annotation which informs Guice – the dependency injection framework used in Slice – that this constructor should be used to create an instance of ArticleModel class. Second, the parameter of the constructor is also annotated. By putting the @CurrentPage annotation here we tell Guice that we require a page model object, but not whichever – we want a page model of a current page. What does Guice do when it sees something like this? It looks for a provider which is supposed to create such an object. And the provider in our case is extremely simple – it’s a method that put in some of Guice modules in my application.

@Provides
@CurrentPage
public PageModel readCurrentPage(@CurrentPagePath String currentPagePath, ModelProvider modelProvider) {
	return modelProvider.get(PageModel.class, currentPagePath);
}

How does it work?

Basically, Slice hides use of Java Use-API and creates the adapter factory for every class annotated by @SliceResource. The adapter factory is able to adapt a resource to your model - it uses standard ModelProvider to get a model from the provided resource.

When an injector is started, Slice scans all of your classes and creates a SliceAdapterFactory object for every SliceResource model. Afterwards, the factory object is being registered as an OSGi service which allows Sightly's Use-API to do its job.

Benefits of using Slice and Sightly together

There are a number of benefits if you use Slice and Sightly together.

Even less code

The amount of code is significantly less if you use Slice. You don’t need to write adapter factories – Slice makes all SliceResource classes adaptable by default without a single line of code. Additionally, thanks to Slice’s mapping capabilities you can read data from the repository extremely easily, simply by putting annotations on fields.

Testability

If you don’t want to write too much code in pure Sightly, you need implement Use interface or extend WCMUse class in your models. Unfortunately, this makes these classes barely testable. Thanks to Slice and its dependency injection pattern, your classes are perfectly testable. What makes them testable are classes which have correct dependencies. By correct I mean dependencies which a class really needs, not something which is required to fetch another object or pass down into class hierarchy. My ArticleModel’s test can be as simple as this:

@RunWith(MockitoJUnitRunner.class)
public class ArticleModelTest {
    @Mock
    private PageModel pageModel;
   
    @Test
    public void testPageTitle() {
        Mockito.when(pageModel.getTitle()).thenReturn("Page title");
        ArticleModel model = new ArticleModel(pageModel);
        assertEquals("Page title", model.getTitle());
        model.setTitle("My title");
        assertEquals("My title", model.getTitle());
    }
}

Simplicity and maintainability

Less code with better dependencies between classes can only mean one thing – your code is simpler. Not only simpler to write but also to read. Because it’s testable at the same time it increases maintainability of your applications significantly.

Re-use of models for Sightly and JSP

JSP has not become deprecated since AEM6 and is not going to be. Therefore you can still write your components using old, good JSP (without scriptlets of course!). You probably also have your legacy components which you want to re-use in newer projects. Thanks to Slice you don’t need to worry about starting new projects with Sightly. Java models written with Slice are exactly the same for JSP and Sightly components. Nothing needs to be changed in the backend to re-use models between different views.

A perfect marriage

The new templating engine - Sightly - is definitely worth using. It introduces a clean separation of concerns and prevents you from writing business logic in your view. Combining it with Slice may be even more beneficial for you and your projects. Thanks to its powerful dependency injection and mapping capabilities you can significantly simplify your code and make it testable. This combination not only offers you a beautiful markup but also a beautiful code which is a perfect match for your applications.