Skip to main content

Sling Servlets 02: What is a Sling Servlet?


Sling Servlet

What is a Sling Servlet?

A Servlet is a class used to extend the capabilities of servers that host applications accessed by means of a request-response programming model.

For such applications, Servlet technology defines HTTP-specific servlet classes.

All servlets must implement the Servlet interface, which defines life-cycle methods. When implementing a generic service, we can use or extend the GenericServlet class provided with the Java Servlet API. The HttpServlet class provides methods, such as doGet() and doPost(), for handling HTTP-specific services.

Sling servlets are a special type of servlets which are registered as OSGi service of type javax.servlet.Servlet. There are some properties defined for Sling Servlets which are as follows -
  • sling.servlet.paths - This is a list of paths under which the servlet is accessible as a Resource. The value can 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 setting of the SlingServletResolver service. Either this property or the sling.servlet.resourceTypes property must be set, or the servlet is ignored. If both are set, the servlet is registered using both ways.
import javax.servlet.Servlet;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Anirudh Sharma
 */
@Component(service = Servlet.class, property = {
  Constants.SERVICE_DESCRIPTION + "=Registered By Path Servlet",
  "sling.servlet.methods=" + HttpConstants.METHOD_GET,
  "sling.servlet.paths=" + "/bin/registeredbypathdemo",
  "sling.servlet.extensions=" + "html"
  })
public class RegisteredByPathExample extends SlingSafeMethodsServlet {

 private static final long serialVersionUID = -5869747502146791269L;

 private final Logger log = LoggerFactory.getLogger(this.getClass());

 @Override
 protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
  log.info("Servlet is being executed via pathss");
 }
}
In the above example, Servlet is only registered by path, so the registration properties sling.servlet.method, sling.servlet.extension has been ignored.
  • 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. Either this property 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.
import javax.servlet.Servlet;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Anirudh Sharma
 */
@Component(service=Servlet.class,
property={
  Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
  "sling.servlet.methods=" + HttpConstants.METHOD_GET,
  "sling.servlet.resourceTypes="+ "redquark/servlets/example",
  "sling.servlet.extensions=" + "html"
 })
public class RegisterByResourceTypeExample extends SlingSafeMethodsServlet {

 private static final long serialVersionUID = 8417083368846819273L;

 private final Logger log = LoggerFactory.getLogger(this.getClass());

 @Override
 protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
  log.info("Executing servlet registered via resource type");
 }
}
  • sling.servlet.resourceSuperTypes - The resource supertype, indicating which previously registered servlet could intercept the request if the request matches the resource super type better. The property value must be a single String. This property is only considered for the registration with sling.servlet.resourceTypes.
  • 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. In case this is not empty the first selector(s) (i.e. the one(s) on the left) in the request URL must match, otherwise, the servlet is not executed. After that may follow arbitrarily many non-registered selectors. 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.
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Servlet;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Anirudh Sharma
 */
@Component(service=Servlet.class,
 property={
   Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
   "sling.servlet.methods=" + HttpConstants.METHOD_GET,
   "sling.servlet.resourceTypes="+ "redquark/servlets/example",
   "sling.servlet.selectors="+"img",
   "sling.servlet.selectors="+"tab"
 })
public class SelectorsPropertyExample extends SlingSafeMethodsServlet {

 private static final long serialVersionUID = 6043799632124866549L;

 private final Logger log = LoggerFactory.getLogger(this.getClass());

 @Override
 protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
  log.info("Executing servlet using selectors");
  try {
   PrintWriter out = response.getWriter();
   out.println("This servlet will be executed via following requests:");
   out.println("http://localhost:4502/content/redquark/example/jcr:content.img.json");
   out.println("http://localhost:4502/contentredquark/example/jcr:content.tab.json");
  } catch (IOException e) {
   log.error(e.getMessage(), e);
  }
 }

}
  • 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.
  • sling.servlet.methods - The request methods supported by the servlet. 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. If this property is missing, the value defaults to GET and HEAD, regardless of which methods are actually implemented/handled by the servlet. A value of * leads to a servlet being bound to all methods. 
  • 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 at 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 the value of this property is a string starting with "/", this value is applied as a prefix, regardless of the configured search paths! If the value is anything else, it is ignored. If this property is not specified, it defaults to the default configuration of the sling servlet resolver.
  • sling.core.servletName - The name with which the servlet should be registered. This registration property is optional. If one is not explicitly set, the servlet's name will be determined from either the property component.name, service.pid or service.id (in that order). This means that the name is always set (as at least the last property is always ensured by OSGi).

Servlet resolution order

The following order rules are being followed when trying to resolve a servlet for a given request URL and request method and multiple candidates would match. Then the following candidate is being picked (if one rule doesn't lead to one winner, the next rule is being evaluated):
  • The one with the highest number of matching selectors + extension.
  • The one which is registered to a resource type closest to the requested one (when traversing the resource type hierarchy up).
  • The one with the highest service.ranking property.
In case of an OptingServlet not matching the next candidate is being used.

Important points

  • For a Servlet registered as an OSGi service to be used by the Sling Servlet Resolver, either one or both of the sling.servlet.paths or the sling.servlet.resourceTypes service reference properties must be set. If neither is set, the Servlet service is ignored.
  • If sling.servlet.methods is not specified, the servlet is only registered for handling GET and HEAD requests. Make sure to list all the methods you want to be handled by this servlet.
  • Each path to be used for registration - either from the sling.servlet.paths property or constructed from the other sling.servlet.\* properties - must be absolute. Any relative path is made absolute by prefixing it with a root path. This prefix may be set with the sling.servlet.prefix service registration property. 
  • If this property is not set, the first entry in the ResourceResolver search path for the ResourceResolver.getResource(String) method is used as the prefix. If this entry cannot be derived, a simple slash - / - is used as the prefix.

Conclusion

In the second part of this series, we learned about Servlets in the Sling framework, their properties, and code examples. You can find the complete code on my GitHub.

I hope you enjoyed this post. Your suggestions and feedback are always welcome. Feel free to befriend me on FacebookTwitter or Linked In or say Hi by email.

Happy Learning 😊

Comments

Post a Comment

Popular posts from this blog

Day 00: AEM Developer Series

Hello everyone! Welcome to this AEM development series. We can all see that the revolution of Digital Marketing today. Companies are dying to be a part of it and they have made this a war for the Digital Marketing tools.

Adobe was way ahead in this war and have gained a lot of market capture. They are leaders in the Digital Marketing platforms since the 2014-15. One of the flagship product in its Digital Marketing suite is Adobe Experience Manager (AEM).

Since AEM is in huge demand, the people who know how to develop on AEM are also in huge demand. But developing on AEM is not easy as it is made up of various open-source technologies such as Apache Felix (OSGi), Apache Sling, Apache Oak and Adobe's own technologies like Granite, HTL etc. Learning all these technologies in conjunction can sometimes become confusing and frustrating 😫.

When I first started learning AEM in 2016, I was dumbfounded to see there is so much going on under the hood. I then spent months to gather all the …

Day 01: Introduction to AEM

Day 04: Developing first OSGi bundle