Using Sling Models API

In this post we will change our contact list application to use the Sling Models API. You can see the first two posts about the app here and here.

By using the AdpterFactory we can adapt Sling objects to our model objects. It is simple, but, we need to have a lot of boilerplate code. The Sling Models bundle allow us to map the Sling objects to our Model objects. It is annotation driven and have a CDI-like style. You can check the documentation here.

Setting up Sling Models in Our Project

The first thing we need to do in order to use the sling models in our core project is to add the required dependencies in our pom.xml.

You can see them below:

    <-- Sling Models API -->
    <dependency>
        <groupId>org.apache.sling</groupId>
        <artifactId>org.apache.sling.models.api</artifactId>
        <version>1.2.2</version>
        <scope>provided</scope>
    </dependency>
    <!-- javax.inject API -->
    <dependency>
        <version>1.0</version>
        <groupId>org.apache.geronimo.specs</groupId>
        <artifactId>geronimo-atinject_1.0_spec</artifactId>
        <scope>provided</scope>
    </dependency>

The first dependency is to allow us to use the Sling Models API, and the second one is to use the annotations from javax.inject package.

The second step is to add the following instructions on the maven-bundle-plugin:

    <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <extensions>true</extensions>
        <version>3.0.0</version>
        <configuration>
            <instructions>
                <Sling-Model-Packages>
                  com.xicojunior.contacts.models
                </Sling-Model-Packages>
            </instructions>
        </configuration>
    </plugin>

With this instructions we set which package will contain our Sling Model POJOs.

After this first set-up let’s code.

Creating our first Sling Model class

To set up our Sling Model class we need to add only some annotations to our POJO. For our first example we will change the Contact class. It will be like below:

@Model(adaptables=org.apache.sling.api.resource.Resource.class)
public class Contact {

    @Inject
    private String name;

    @Inject
    private String email;

    @Inject
    private String phone;

    @Inject
    private String address;

Basically we define that our class will be a Sling Model by using the @Model annotation. In that annotation we define the adaptable class, in our example Resource, so we can adapt an instance of Resource to a Contact instance.

Then we use the @Inject annotation to inject the node properties into the object attributes. The class properties have the same name as the node properties. So, when we deploy our bundle and use:

resource.adaptTo(Contact.class);

It will inject the properties which name matches with the annotated properties in the class.

Great, but if we remember of our last posts, it is missing the contact image. The image is a child node under the contact node. For this we will use some new annotations, let’s see how to inject the child node into our class:

@ChildResource @Named("contactImage")
private Resource contactImageResource;

private String contactImage;

@PostConstruct
public void init() {
    this.contactImage = contactImageResource.getPath();
}

Let’s explain what we’ve done in the code above:

We used the @ChildResource in order to inject a child resource of the resource being adapted from. As the node name is not the same as the class property, we used the @Named with the node name. This way we inject the node as a Resource in our class. But in our JSP, we only want the Resource path in order to display the image on the page, the JSP uses the contactImage property.

In order to keep the same attribute and not change the JSP, we created the init method and annotated it with @PostConstruct. With this annotation we are saying that this method will be executed after the object be instantiated. In our example it basically set the contactImage property with the contact image resource path.

Great! Now we can adapt the contact nodes to our Contact class with much less code.

Now we need to change our ContactList to use the Sling Models API.

Changing the ContactList model class

The next step we are going to take is to change our ContactList class to be a Sling Model. And it is very simple. Basically we needed to add 3 annotations as we can see below:

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

    @Inject @Via("resource")
    private List<Contact> contacts;

We added the @Model now having SlingHttpServletRequest as the adaptable. This class only holds the Contacts created. To inject the contact list we just needed to add the @Inject in the contacts property. With the @Inject annotation we can also inject a list of child resources. In our case, it will inject all child nodes of the contacts node.

One other thing we need to do is to add the @Via annotation. This annotation indicates that the injection should be done via a JavaBean property of the adaptable, in our example:

slingRequest.getResource()

And as we turned our Contact class into a Sling Model, we don’t need to adapt from the child Resources, the Sling Models API does that for us automatically :).

Conclusion

The Sling Models API provides us a very simple way to convert Sling Objects into our model classes. In this post we showed only some of the features provided by the API, it has a lot more to features that makes a lot easier our work.

The complete code used in this post is on github. In order to deploy it you just need to do the following:

git clone https://github.com/fjunior87/sling-contact-list.git
cd sling-contact-list
git checkout sling_models
mvn clean install -P autoInstallBundle

And access the application.

http://localhost:8080/contactsapp.html

To create the nodes you need to be authenticated, so, before use the application, please log in in the page:

http://localhost:8080/.explorer.html

That’s it for today, I hope you enjoyed it.

Thanks and see you in the next post.

comments powered by Disqus