Ad Code

Sunday, August 20, 2017

Deep Dive on Sling Model in AEM 6.3 : Part-2


In our previous blog, we have already learned basics of sling models.But the magic of sling models is not over. We have more to know.
We have seen injector specific annotations of sling models in Part-1, except all these specific sling model Injectors we have some more annotations that help in Sling Models Development.
@Inject: This annotation is used to inject a property, resource, request anything.This is a generic annotation, which traverses all the sling model injectors based on service ranking.

@Model(adaptables = Resource.class)
public classTest {

  @Inject
  String path;

  public String getPath() {
      return path;
  }
}

@Named: If there is a need to change the getter of any attribute like (sling:resourceType, jcr:primaryType) @Named annotation helps to achieve this.

@Model(adaptables = Resource.class)
public class Test {

  @Inject @Named("sling:resourceType")
  String slingResourceType;

  public String getSlingResourceType() {
      return slingResourceType;
  }
}

@Default: A default value can be provided for Strings or primitive data types.If there is no value of that property, default value takes place.

@Model(adaptables = Resource.class)
public class Test {

  @Inject @Default(values = "/content/test")
  String path;

  public String getPath() {
      return path;
  }
}

@Optional and @Required: In the sling models, by default all the fields supposed to be required.Sometimes there is a need to mark them as optional and required specifically.So injector fields can be annotated with @Optional and @Required.

If a majority of @Injected fields/methods are optional, it is possible to change the default injection strategy by using adding defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL to the @Model annotation:

@Model(adaptables = Resource.class)
public class Test {

  @Inject @Optional
  String path;

  @Inject @Required
  String title;

  public String getTitle() {
      return title;
  }

  public String getPath() {
      return path;
  }
}
@Source: If you are using @Inject annotation and you want to specifically tell the sling engine that which specific injector you want to inject, you can use this annotation.All the specific injectors can also be used with @Source annotations.Using this annotation is equivalent to using injector specific annotation in a different way.

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

  @Inject  @Source("script-bindings")
  Page currentPage;

  public String getPath() {
      return currentPage.getPath();
  }
}
@Via : SlingHttpServletRequest has more objects than resource. Sometimes there is a need of using two injectors one from request and one from resource, Then we need to tell annotation explicitly that you are coming via resource.

@Model(adaptables = SlingHttpServletRequest.class)
public class TestModel {
   
   // Injects ResourcePath with Via annotation
   @ResourcePath(path = "/etc/social") @Via("resource")
   Resource resource;
   
   // Injects currentPage from ScriptVariable adaptable to Request
   @ScriptVariable
   Page currentPage;
}
@PostConstruct: The @PostConstruct annotation can be used to add methods which are invoked upon completion of all injections: This method automatically gets called when a sling model instance is created.
Note:The name of the method doesn’t matter, only it is matters that on which method the PostConstruct annotation exist.

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

 @Inject @Source("script-bindings")
 Page currentPage;
 
  String path;

 @PostConstruct
 protected void postMethod() {
     path = currentPage.getPath();
 }

 public String getPath() {
     return path;
 }
}
The Demonstration video on @Inject, @Named, @Default, @Optional, @Required, @Source, @Via and @PostConstruct:


@AemObject Annotation by ACS Commons Package

ACS Commons package provides one more injector specific annotation named @AemObject.This annotation provides the support of a lot of objects shown below:
aemObject.PNG
Fig- List of object in @AemObject annotation provided by ACS Common
This annotation is not the part of sling models, so if sling models itself has all your needed injectors, no need to go for it.But some Objects are not available with sling models like Tagmanager, WorkflowSession, WCMMode, at that time, this annotation can help you. Remember your project must have dependencies of ACS-Commons before using this annotation.

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

// Injects currentPage using ScriptVariable annotation
@AemObject
Page currentPage;

public String getPagePath() {
 currentPage.getPath();
}
}

List Injection From Child Resource(Since Sling Models Impl 1.0.6)
+- resource (being adapted)
|
+- content
   |
   +- subchild1
   |
   +- subchild

@Model(adaptables = Resource.class)
public classTest {

  @Inject
  List<Resource> content;
  
 public int getSize()
  {
      return content.size();
  }
}
Sling Adapter Framework
1.adaptTo: Apache Sling provides a way to adapt Sling related classes to our domain classes. The Resource and ResourceResolver interface provides the adaptTo method, which adapts the objects to other classes.
With the adaptTo API we can convert the Sling-related objects to our Model objects by using the AdapterFactory classes.
Test model = resource.adaptTo(Test .class)
As with other AdapterFactories, if the adaptation can't be made for any reason, adaptTo() returns null.
2.ModelFactory (Since1.2.0): Since Sling Models 1.2.0 there is another way of instantiating models. The OSGi service ModelFactory provides a method for instantiating a model that throws exceptions.There is no need of null checks and it is easier to see why sling model instantiation is failed. ModelFactory API provides a lot of methods, which can be efficiently used.

public class ModelServlet extends SlingSafeMethodsServlet {

@Reference
ModelFactory modelFactory;

@Override
protected void doGet(final SlingHttpServletRequest req,final SlingHttpServletResponse resp) throws ServletException, IOException {

 Resource resource = req.getResourceResolver().getResource("/content/community-components/en/tagcloud/jcr:content");

 Test test = modelFactory.createModel(resource, Test.class);
 resp.getWriter().println(test.getResourceType());
 resp.getWriter().println(modelFactory.canCreateFromAdaptable(resource, Test.class));
 resp.getWriter().println(modelFactory.canCreateFromAdaptable(req, Test.class));

}
}

@Model(adaptables = Resource.class)
public class Test {

 @Inject @Named("sling:resourceType")
  String resourceType;

  public String getResourceType()
  {
      return resourceType;
  }
}

The Demonstration video on @AemObject, List Injection and Adapter Framework:



If you have any query or suggestion then kindly comment or mail us at sgaem.blog02@gmail.com

Hope it will help you guys !!
Thanks and Happy Learning.

11 comments:

  1. Thanks a lot for the blog.

    ReplyDelete
  2. can you please tell in depth about the differences of using different adaptable such as @Model(adaptables=resource.class) and @Model(adaptables=slingHttpServletRequest.class).

    Thanks

    ReplyDelete
    Replies
    1. Whether you use one adaptable or the other (or both at once) depends on what you need in your model.If you need to read some values from the resource then use "adaptables=resource.class" and if we need to read some values from request then use "adaptables=slingHttpServletRequest.class" . If you need values from both then use both like "adaptables = {SlingHttpServletRequest.class, Resource.class}";

      Delete
  3. very clear explanation...thanks a lot for the post

    ReplyDelete
  4. I am trying to adapt ContentFragmentList sling model in my sling model.

    ContentFragmentList fragmentList = slingHttpServletRequest.adaptTo(ContentFragmentList.class);

    but it throw below error -

    Caused by: java.lang.ClassCastException: com.adobe.cq.wcm.core.components.internal.models.v1.contentfragment.ContentFragmentListImpl cannot be cast to com.adobe.cq.wcm.core.components.models.contentfragment.ContentFragmentList

    ReplyDelete
  5. Hi Everyone,

    Can you please tell me advantage of getting the values using models rather than ${properties.Nodename}

    ReplyDelete
  6. great job so much helpful

    ReplyDelete
  7. How do we take third party service api in component ?

    ReplyDelete