Ad Code

Friday, December 29, 2017

Apache Sling : Servlets and Scripts in AEM 6.3

Hello Everyone,

Sling Servlets is one of the very useful concepts in AEM. How much we know and use it, It has a lot more to know about it.
So In this blog, I will start with basics and will walk you through all the options and varieties of Sling Servlets with hands-on examples.


Servlet Registration
To register a servlet the following properties play a vital role.
1. sling.servlet.paths: A list of absolute paths under which the servlet is accessible as a Resource. The property value must either be a single String, an array of Strings or a Vector of Strings.
A servlet using this property might be ignored unless its path is included in the Execution Paths (servletresolver.paths) configuration(Apache Sling Servlet/Script Resolver and Error Handler) setting of the SlingServletResolver service.

@Component(service = Servlet.class,
      property = {
              Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
              "sling.servlet.methods=" + HttpConstants.METHOD_GET,
              "sling.servlet.paths=" + "/bin/servlet",
              "sling.servlet.extensions=" + "sample",
      })
public class ResolveServletUsingPath extends SlingSafeMethodsServlet {
  @Override
  protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
    
}}

This Servlet service registered with these properties is registered under this path: /bin/servlet

Note: In the above example, Servlet is only registered by path, so the registration properties sling.servlet.method, sling.servlet.extension has been ignored.
2. sling.servlet.resourceTypes:The resource type(s) supported by the servlet. The property value must either be a single String, an array of Strings or a Vector of Strings.
Note:Either this property(sling.servlet.resourceTypes) or the sling.servlet.paths property must be set, or the servlet is ignored. If both are set, the servlet is registered using both ways.
Fig - Register the servlet using resource type

@Component(service=Servlet.class,
      property={
              Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
              "sling.servlet.methods=" + HttpConstants.METHOD_GET,
              "sling.servlet.resourceTypes="+ "community-components/components/componentpage",
              "sling.servlet.extensions=" + "sample",
      })
public class MyServlet extends SlingSafeMethodsServlet {

      @Override
      protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)throws IOException
      {
         
      }
  }

3. sling.servlet.selectors:The request URL selectors supported by the servlet. The selectors must be configured as they would be specified in the URL that is as a list of dot-separated strings such as print.a4. The property value must either be a single String, an array of Strings or a Vector of Strings. This property is only considered for the registration with sling.servlet.resourceTypes.

@Component(service=Servlet.class,
      property={
              Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
              "sling.servlet.methods=" + HttpConstants.METHOD_GET,
              "sling.servlet.resourceTypes="+ "community-components/components/componentpage",
              "sling.servlet.selectors="+"img",
              "sling.servlet.selectors="+"tab",
      })
public class MyServlet extends SlingSafeMethodsServlet {

      @Override
      protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)throws IOException
      {
  }
}

The request can be:
http://localhost:4502/content/community-components/en/tagcloud/jcr:content.img.json
http://localhost:4502/content/community-components/en/tagcloud/jcr:content.tab.json
4. sling.servlet.extensions:The request URL extensions supported by the servlet for requests. The property value must either be a single String, an array of Strings or a Vector of Strings. This property is only considered for the registration with sling.servlet.resourceTypes.


5. sling.servlet.methods: The request methods supported by the servlet. This property is only considered for the registration with sling.servlet.resourceTypes. If this property is missing, the value defaults to GET and HEAD, regardless of which methods are actually implemented/handled by the servlet.
6. sling.servlet.prefix: The prefix or numeric index to make relative paths absolute. If the value of this property is a number (int), it defines the index of the search path entries from the resource resolver to be used as the prefix.
The defined search path is used as a prefix to mount this servlet. The number can be -1 which always points to the last search entry. If the specified value is higher than than the highest index of the search paths, the last entry is used. The index starts with 0. If the value of this property is a string and parseable as a number, the value is treated as if it would be a number. If this property is not specified, it defaults to the default configuration of the sling servlet resolver.
So if:
  • prefix=0 or prefix=/apps/, then it will accept the default request(relative path of resourceType) and the resourceType starts with “/apps”.because /apps is the first index of search path entry.
  • prefix=1 or prefix =/libs/ then it will accept the default request(relative path of resourceType) and the resourceType starts with “/libs”
  • the default scenario is prefix=-1, then it will accept the default request(relative path of resourceType) and the resourceType starts with “/libs” because /libs is last search entry.

Note:Binding resources by paths is discouraged.
Always try to register servlet using resourceTypes in place of paths.You can use selectors and extension to uniquely identify a servlet.

@Component(service=Servlet.class,
      property={
              Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
              "sling.servlet.methods=" + HttpConstants.METHOD_GET,
              "sling.servlet.resourceTypes="+ "sling/servlet/default",
              "sling.servlet.selectors="+ "data",
              "sling.servlet.extensions="+ "sample"

      })
public class MyServlet extends SlingSafeMethodsServlet {

      @Override
      protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)throws IOException
      {
      }
}

The request can be:
http://{server host:server port}/{any path}.data.json

The value of “sling.servlet.resourceTypes” is “sling/servlet/default” to handle all kinds of requests having any path.We can also consider it as a default resourceType.


There is an OSGi Configuration named Apache Sling Servlet/Script Resolver and Error Handler in the felix console
Fig - Servlet/Script resolver configuration
There are four options in this configuration:
  • Servlet Registration Root Path:If the servlet don’t have prefix, the value of prefix need to be picked from this configuration.
  • Cache Size: This property configures the size of the cache used for script resolution.To see the scripts which are being cached
    1. Go to the Felix console.
    2. Go to Sling->Script Cache Status
Fig - Check Sling Cache Status in felix console
The cache of script resolution can be seen here:

Fig - Check Cached Script in felix console
  • Execution Paths: The paths to search for executable scripts. This configuration means : All the paths starts with the paths they have provided will be allowed.
If no path is specified, this will be treated like (/=root) which allows all scripts. If we add one path without ending with /, then it means it will only allow exact path.
  • Default Extension:The list of extensions for which the default behavior will be used.
There is a sling resolver test provided by Felix console where we can check that a particular request resolves to which servlet.
  1. Go to felix console.
  2. Go to Sling-> Sling Servlet Resolver
Fig - Servlet Resolver option in felix 

Fig - Check servlet is resolving or not
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.

Sunday, December 17, 2017

Deep Dive in Sling Models in AEM 6.3 : Part-3


Hi,

I am overwhelmed with a very good response from my previous blogs over sling model and
when i am really going more in deep of this concept, there are a lot of things which can ovewhelm you more.

Our journey of Sling Models is not over yet and we have something really interesting to know more.

Registration of Sling Models Via Bnd Plugin (Since Sling Model Impl 1.3.4)

When creating sling Models, there is an important step of adding the sling Model package in maven-bundle plugin. This plugin generates Sling-Model Classes header.

Now through bnd plugin you can get rid of adding <Sling-Model package> in maven-bundle-plugin.

You just neet to add <_plugin>org.apache.sling.bnd.models.ModelsScannerPlugin</_plugin> in the configuration tag.


<plugin>
   <groupId>org.apache.felix</groupId>
   <artifactId>maven-bundle-plugin</artifactId>
   <extensions>true</extensions>
   <configuration>
       <instructions>
          <_plugin>org.apache.sling.bnd.models.ModelsScannerPlugin</_plugin>
       </instructions>
   </configuration>
   <dependencies>
       <dependency>
           <groupId>org.apache.sling</groupId>
           <artifactId>org.apache.sling.bnd.models</artifactId>
           <version>1.0.0</version>
       </dependency>
   </dependencies>
</plugin>

This will scan all the Sling Models by itself. If Sling Model package or Sling model Classes are  already manually defined,bnd plugin will not work.
Note: Don't define Sling model package manually if you are using bnd plugin.

If you want to generate a bundle header compliant with Sling Models < 1.3.4 (i.e. Sling-Model-Packages) you need to specify the attribute generatePackagesHeader=true. An example configuration looks like this 

<configuration>
   <instructions>          <_plugin>org.apache.sling.bnd.models.ModelsScannerPlugin;generatePackagesHeader=true</_plugin>
   </instructions>
</configuration
Demonstration Video On Registration of Sling Model Via Bnd Plugin:

Caching

By default, Sling Models don’t have any caching of the adaption result.There was a big problem that adaption of every request for a model class will create a new instance of that model.

There are two cases when the adaption results can be cached.
  • The first case is when the adaptable extends SlingAdaptable base class.This case is for the resources adaptables as AbstractResource extends SlingAdaptable.

Sling Adaptable has a caching mechanism such that when we do multiple time adaption (adaptTo)of same resource will return the same object.

res.PNG

By default two objects of same sling models adapted from a resource are same.
TestModel testModel1 = resource.adaptTo(TestModel.class);
TestModel testModel2 = resource.adaptTo(TestModel.class);

Assert testModel1 == testModel2
request.PNG
  • This is notably not in the case of SlingHttpServletRequest as this class doesn’t extend SlingAdaptable.

By default two objects of same sling models, adapted from a request are false.



TestModelRequest testModelRequest1 = request.adaptTo(TestModelRequest.class);
TestModelRequest testModelRequest2 = request.adaptTo(TestModelRequest.class);

Assert testModelRequest1 == testModelRequest2

Since Sling Models API version 1.3.4 , Sling Models can cache adaptable results regardless of the adaptable by specifying cache=true in @Model annotation.

If we put the TestModelRequest Sling Model like this:

@Model(adaptables = SlingHttpServletRequest.class,cache = true)
public class TestModelRequest {

TestModelRequest testModelRequest1 = request.adaptTo(TestModelRequest.class);
TestModelRequest testModelRequest2 = request.adaptTo(TestModelRequest.class);

Assert testModelRequest1 == testModelRequest2
}

Note: When cache = true is specified, the adaptation result is cached regardless of how the adaptation is done. Read more about Sling Adaption Framework .

Specifying an Alternative Adapter Class(Since Sling Model Impl 1.1.0)

By default a Sling Model is registered with its own implementations.But @Model annotation also has an attribute named adapters which allows specifying under which type(s) the model implementation should be registered in the Models Adapter Factory. Prior to Sling Models Impl 1.3.10 only the given class names are used as adapter classes, since 1.3.10 the implementation class is always being registered implicitly as adapter as well.

With this attribute it is possible to register the model to one (or multiple) interfaces, or a superclass. This allows separating the model interface from the implementation, which makes it easier to provide mock implementations for unit tests as well.

public interface A{
  public String getName();
}
public interface B{
  public String getPath();
}

@Model(adaptables = Resource.class, adapters = {A.class, B.class})
public class TestAdapter implements A, B {
  // injects fields and implements the A and B interface's methods
  public String getName() {
      return "SGAEM";
  }

  public String getPath() {
      return "sgaem.blogspot.in";
  }
}

Let’s demo it for an amazing understanding:


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.