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.

7 comments:

  1. Can you show the imports you are using for the examples, we are tying to find what library you use for the component anotation

    ReplyDelete
    Replies
    1. import org.osgi.service.component.annotations.Component;
      import org.osgi.service.component.annotations.Reference;

      Delete
  2. Pleas if you can share the code would be helpful ... But that was really excellent

    ReplyDelete
  3. Hi Shivani,
    Can you please share the full sample code for all these different scenarios (may be a GitHub url?). That would be very useful.

    ReplyDelete
  4. Hi Shivani,
    Is there any way to end workflow(sample code) automatically after sending notification to initiator in request for activation workflow.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete