Deep diving into Sling models PT2

Deep diving into Sling models PT2

Sling Models part 2: Annotate your Sling models
19 June 2020

Part 2: How to properly annotate your Sling models

In this Sling models PT2 blog post, I will show you how to properly annotate your Sling models.
There are two types of developers in this world: those who care about clean code and those who don’t. I like to believe that I’m part of that first group.

Throughout the projects that I’ve worked on, I’ve often seen some mad things in source code that I believe is the result of developers being either extremely lazy or not knowing any better due to a lack of knowledge/training. Here is a list of things I believe to be wrong when you create Sling models:

  • Set the defaultInjectionStrategy to optional
  • Use both SlingHttpServletRequest and Resource as adaptables
  • Use @Inject everywhere
  • No name added to the property using @Named

Let me illustrate with an example:

package com.mycompany.models;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import javax.inject.Inject;
@Model(adaptables = {Resource.class, SlingHttpServletRequest.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class DontDoItLikeThisModel {
    
    @Inject
    private Resource resource;
    
    @Inject
    private String property;
    
    @Inject
    private ResourceResolverFactory resourceResolverFactory;
    
    //all of the methods associated with this model
}

I challenge you to go through your current codebase and have a good look at the models. I bet there’s an 80% chance you’ll find instances of all the things I summed up earlier!

So, what would be my recommendations here?

  • NEVER set defaultInjectionStrategy to optional, not even IF all of your properties can be optional, because this means that your Resource/SlingHttpServletRequest or any service that you want to inject in your model is also optional. More often than not, you will not be null-checking these, which results in you potentially getting NullPointerExceptions.
  • Choose between Resource or SlingHttpServletRequest. You cannot adapt from both, because if you look at the example code I provided, if you would adapt from a SlingHttpServletRequest, then that resource property can never contain any value. I usually use the following rule: go with Resource, until you require something specifically from the request.
  • Instead of using @Inject, use an appropriate annotation. Here’s a list of which annotation to use for each scenario:
  • If you still want to use @Inject to inject a property from a node, then combine that with @Named to ensure that you are always injecting the same property from that node. This approach makes your code backwards compatible if you ever decide to make a change to the property in your Sling model class. This is because by default it will look at the name of your java property to see check which property of the JCR node it has to inject. If you use @ValueMapValue, you can choose between using the name property of the annotation or using the @Named annotation

Here’s an example of a properly annotated Sling model (at least, in my humble opinion):

package com.mycompany.models;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
@Model(adaptables = {Resource.class})
public class ProperlyAnnotatedModel {
    private static final String PROPERTY_NAME = "propertyName";
    @Self
    private Resource self;
    @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = PROPERTY_NAME)
    private String property;
    @OSGiService
    private ResourceResolverFactory resourceResolverFactory;
    //all of the methods associated with this model
}

Keep in mind that if you use this model for one specific component, you need to make sure to add a resourceType attribute to your @Model annotation. To see why, please read my previous blog post “Creating an interface for a model”. If you’re wondering why I didn’t create an interface here, that’s because this post is specifically about annotations. I would always advice to create an interface for any Sling model.

And there you have it: my personal view on how to properly annotate your Sling models. I hope that this has been informative to you and if you still have some questions, feel free to get in touch and we will gladly help you out.

Tip: Digitalum helps organizations to benchmark and advises on tooling architecture. Book time with an expert or contact us.

Need help with your project?

Digitalum, SAP Customer Experience, Cloud, Meet our Team, Contact, E-commerce strategy,

Most recent Insights

“Tell me and I forget. Teach me and I remember. Involve me and I learn.” We adopt a practical approach to learning, inspired by Benjamin

Lees verder »
Data Security and AI

Data security: why is it crucial? Whatever your activities, chances are that you’re collecting and using a lot of confidential data. You’re sitting on a

Lees verder »
SAP Emarsys

Increase customer engagement with SAP Emarsys SAP Emarsys is a customer engagement tool that will help you reach customers with the right message, at just

Lees verder »
3 juniors working at Digitalum

Q: How did you guys end up at Digitalum? Sam: “I’m 21 years old and have a degree in digital design. So my focus was

Lees verder »