April 10, 2020
Estimated Post Reading Time ~

Working with Sling Servlets in AEM

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

In Sling, servlets can be registered as services. Whenever you create an AEM Multimodule Project using maven archetype 14, the default template for a Sling Servlet is generated as follows - 

/*
 *  Copyright 2015 Adobe Systems Incorporated
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.redquark.demo.core.servlets;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletException;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;

import com.day.cq.commons.jcr.JcrConstants;

/**
 * Servlet that writes some sample content into the response. It is mounted for
 * all resources of a specific Sling resource type. The
 * {@link SlingSafeMethodsServlet} shall be used for HTTP methods that are
 * idempotent. For write operations use the {@link SlingAllMethodsServlet}.
 */
@Component(service = Servlet.class,
 property = {
  Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
  "sling.servlet.methods=" + HttpConstants.METHOD_GET,
  "sling.servlet.resourceTypes=" + "demoproject/components/structure/page",
  "sling.servlet.extensions=" + "txt"
 })
public class SimpleServlet extends SlingSafeMethodsServlet {

 private static final long serialVersionUID = 1 L;

 @Override
 protected void doGet(final SlingHttpServletRequest req,
  final SlingHttpServletResponse resp) throws ServletException, IOException {
  final Resource resource = req.getResource();
  resp.setContentType("text/plain");
  resp.getWriter().write("Title = " + resource.getValueMap().get(JcrConstants.JCR_TITLE));
 }
}

Let us discuss its parts one by one
  • @Component annotation - This OSGi annotation signifies that the class is a Service component and is not processed at runtime by a Service Component Runtime (SCR) implementation. It must be processed by tools and used to add a Component Description to the bundle.
  • service property - This signifies the types under which to register this Component as a service. In our case, the component is registered under the Servlet type. 
  • Constants.SERVICE_DESCRIPTION - Defines standard names for the OSGi environment system properties, service properties, and Manifest header attribute keys. In our case, we are defining the description of our servlet.
  • sling.servlet.methods - Defines which methods this servlet will use. In our case, we are using the HTTP GET method.
  • sling.servlet.resourceTypes - This property defines how are we accessing the servlet. There are two ways - via resource types and via paths. We will discuss this in the next section.
  • 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.

Servlet Registration

SlingServletResolver listens for Servlet services and - given the correct service registration properties - provides the servlets as resources in the (virtual) resource tree. Such servlets are provided as ServletResource instances which adapt to the javax.servlet.Servlet class.

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. 

A Sling servlet can be registered in two ways - 

  1. Using Resource Types - Using this way, we use the sling:resourceType property of the node. For this, we need to hit the path in the browser for which the sling:resourceType is the given one.
  2. Using Paths - Using this way, we can directly use the path specified in the request and our servlet will be executed.
Registering a servlet via a path is relatively easier than registering it via a resource type but it is still recommended to use resource types instead of paths. Below are the reasons why - 
  • If we use paths, then we need to be extra careful as we do not want to give any path randomly. The servlets cannot be access-controlled using the default JCR ACLs.
  • No suffix handling in the path bound servlets.
  • We also need to specify the paths to the servlet consumers and change in the path can have a serious impact.

Types of Servlets

There are two types of servlets in Sling which are nothing but the classes we need to extend while creating our servlet.
  • SlingSafeMethodsServlet - If we want to use only the read-only methods then we use this. This base class is actually just a better implementation of the Servlet API HttpServlet class which accounts for extensibility. So extensions of this class have great control over what methods to overwrite. It supports GET, HEAD, OPTIONS, etc methods.
  • SlingAllMethodsServlet - If we want to use methods that write as well, then we use this. This class extends the SlingSafeMethodsServlet by support for the POST, PUT and DELETE methods.

Sling Servlet Example

In this section, we will be creating our custom Sling Servlet to fetch the JSON data from a RESTful web service.

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.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import static org.redquark.demo.core.constants.AppConstants.URL;
import org.redquark.demo.core.utils.Network;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Anirudh Sharma
 * 
 * This servlet uses the HTTP GET method to read a data from the RESTful web services
 */
@Component(service = Servlet.class, property = {
 Constants.SERVICE_DESCRIPTION + "=JSON Servlet to read the data from the external webservice",
 "sling.servlet.methods=" + HttpConstants.METHOD_GET,
 "sling.servlet.paths=" + "/bin/readjson"
})
public class JSONServlet extends SlingSafeMethodsServlet {

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

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

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

  try {

   log.info("Reading the data from the webservice");

   /**
    * Getting the JSON string from the webservice
    */
   String responseString = Network.readJson(URL);

   /**
    * Writing the entire JSON string on the browser
    */
   response.getWriter().println(responseString);

  } catch (Exception e) {

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

}

As you can see that we are using Network.java and AppConstants.java in the code.

After deploying the code on your AEM server hit the URL http://<host>:<port>/bin/readjson. You will see the JSON string returned on your screen.


By aem4beginner

No comments:

Post a Comment

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