April 10, 2020
Estimated Post Reading Time ~

Getting to know Sling Models

Wouldn't it be great if you have a direct way to map your JCR node properties to your model class? I guess it would be. In AEM, we can achieve this via Sling Models.
Sling Models are "pure" POJOs that maps Sling objects (resources, request objects, etc.).
Since Sling Models are annotation-driven Plain Old Java Objects (POJOs), annotations are used a lot. They allow you to map resource properties, assign default values, inject OSGi services and much more.

Without making a further delay, let's see the concepts in action.

Working with Sling Models

For AEM 6.2

  • Download the latest Sling Models API and Implementation bundles from here and upload them to ./system/console/bundles
  • In the project’s pom files, make sure the version numbers for Sling Models API and Implementation are updated based on the ones installed in your AEM server.
  • In your core module pom.xml file, check for maven-bundle-plugin, and make sure you have all packages that contain the model classes or interfaces in header Sling-Model-Packages, so that your models can be picked up.

For AEM 6.3+

  • Since AEM 6.3 is built on Sling 1.3, we don't have to install any bundles in our AEM server.
  • But we need to make sure that the Sling Models API version in AEM matches with the one in our project's pom files.

Example

Now let us look at an example for sling models 
  • Create a new AEM project using AEM Multimodule Project and deploy it in your AEM instance.
  • Go to CRXDE and under your project in /apps create a new component with the following configuration
Create a Component
  • Now create the cq:dialog node under the component node with the following configuration.
<?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" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="User Component"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/foundation/container">
        <layout
            jcr:primaryType="nt:unstructured"
            sling:resourceType="granite/ui/components/foundation/layouts/tabs"
            type="nav"/>
        <items jcr:primaryType="nt:unstructured">
            <properties
                jcr:primaryType="nt:unstructured"
                jcr:title="User Properties"
                sling:resourceType="granite/ui/components/foundation/container">
                <layout
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
                <items jcr:primaryType="nt:unstructured">
                    <columns
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/foundation/container">
                        <items jcr:primaryType="nt:unstructured">
                            <firstName
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/foundation/form/textfield"
                                class="field-whitespace"
                                fieldDescription="Please enter the first name"
                                fieldLabel="First Name"
                                name="./firstName"/>
                            <lastName
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/foundation/form/textfield"
                                class="field-whitespace"
                                fieldDescription="Please enter the last name"
                                fieldLabel="Last Name"
                                name="./lastName"/>
                            <gender
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/foundation/form/select"
                                fieldDescription="Select your grnder"
                                fieldLabel="gender"
                                name="./gender">
                                <items jcr:primaryType="nt:unstructured">
                                    <male
                                        jcr:primaryType="nt:unstructured"
                                        text="Male"
                                        value="Male"/>
                                    <female
                                        jcr:primaryType="nt:unstructured"
                                        text="Female"
                                        value="Female"/>
                                    <other
                                        jcr:primaryType="nt:unstructured"
                                        text="Other"
                                        value="Other"/>
                                </items>
                            </gender>
                            <country
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/foundation/form/select"
                                fieldDescription="Select your country"
                                fieldLabel="Country"
                                name="./country">
                                <items jcr:primaryType="nt:unstructured">
                                    <option1
                                        jcr:primaryType="nt:unstructured"
                                        text="India"
                                        value="India"/>
                                    <option2
                                        jcr:primaryType="nt:unstructured"
                                        text="Canada"
                                        value="Canada"/>
                                    <option3
                                        jcr:primaryType="nt:unstructured"
                                        text="Israel"
                                        value="Israel"/>
                                    <option4
                                        jcr:primaryType="nt:unstructured"
                                        text="Other"
                                        value="Other"/>
                                </items>
                            </country>
                        </items>
                    </columns>
                </items>
            </properties>
        </items>
    </content>
</jcr:root>
  • Create a new class UserModel.java in your project and paste the following code into it
package org.redquark.demo.core.models;

import javax.inject.Inject;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;

/**
 * @author Anirudh Sharma
 * 
 * User Component model class
 */
@Model(adaptables = Resource.class)
public class UserModel {

 @Inject
 private String firstName;

 @Inject
 private String lastName;

 @Inject
 private String gender;

 @Inject
 private String country;

 /**
  * @return the firstName
  */
 public String getFirstName() {
  return firstName;
 }

 /**
  * @return the lastName
  */
 public String getLastName() {
  return lastName;
 }

 /**
  * @return the gender
  */
 public String getGender() {
  return gender;
 }

 /**
  * @return the country
  */
 public String getCountry() {
  return country;
 }

}
  • Let us understand the code of the class. We are using @Model annotation to specify that this model class is a Sling Model. Each data member is annotated with @Inject. This class will be mapped to a resource in JCR. 
  • Drag and drop the component on the page and configure it
Configure the component
  • After saving the component, the data will save under the page node as shown in the figure
Data stored in JCR

  • To access this resource in the JCR, let us create a Sling Servlet and paste the following code in it.
package org.redquark.demo.core.servlets;

import javax.servlet.Servlet;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.redquark.demo.core.models.UserModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Anirudh Sharma
 * 
 * Servlet to consume the Sling Model
 */
@Component(service = Servlet.class, property = {
 Constants.SERVICE_DESCRIPTION + "=Sling Demo Servlet",
 "sling.servlet.methods=" + HttpConstants.METHOD_GET,
 "sling.servlet.paths=" + "/bin/slingmodel/user"
})
public class SlingModelServlet extends SlingSafeMethodsServlet {

 /**
  * Generated serialVersionUID
  */
 private static final long serialVersionUID = 7558680464517017317 L;

 /**
  * Logger
  */
 private static final Logger log = LoggerFactory.getLogger(SlingModelServlet.class);

 /**
  * Overridden method
  */
 @Override
 protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {

  try {

   log.info("----------< Processing starts >----------");

   /**
    * Getting the instance of resource resolver
    */
   ResourceResolver resourceResolver = request.getResourceResolver();

   /**
    * Getting the resource which has the data stored
    */
   Resource resource = resourceResolver
    .getResource("/content/we-retail/language-masters/en/user/jcr:content/root/responsivegrid/user");

   /**
    * Adapting the resource to the UserModel class
    */
   UserModel model = resource.adaptTo(UserModel.class);

   /**
    * Printing the value stored on the browser window
    */
   response.getWriter()
    .print("Data stored in the resource is:\nFirst Name: " + model.getFirstName() + "\nLast Name: " +
     model.getLastName() + "\nGender: " + model.getGender() + "\nCountry: " +
     model.getCountry());

   /**
    * Closing the resource resolver
    */
   resourceResolver.close();

  } catch (Exception e) {

   log.error(e.getMessage(), e);
  }

 }

}
  • In line #54-55, we are getting the resource and in line #60, we are adapting the resource to our UserModel class
  • Note that we are not setting any data in the model object, we are just calling the getters to access the data stored in the JCR.
  • Now, deploy the code in your AEM server (how? see here) and hit the request - http://<host>:<port>/bin/slingmodel/user you will see the following result.
                           Data stored in the resource is:
                           First Name: Anirudh
                           Last Name: Sharma
                           Gender: Male
                           Country: India
  • As you see, we can directly access a Sling Resource using Sling Model classes. This saves us a lot of boilerplate code and lets us do separation of concerns. 


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.