April 6, 2020
Estimated Post Reading Time ~

Overriding Out Of The Box Servlet In AEM (Sling)

Technical Context
In AEM (CQ), servlets are defined as OSGi services implementing javax.servlet.Servlet interface. A service listener org.apache.sling.servlets.resolver.internal.SlingServletResolver in sling framework listens for OSGi services implementing javax.servlet.Servlet interface and registers its reference. When a request hits the server, this SlingServletResolver kicks in to executes its sling script resolution procedure to choose the right servlet to respond to the request.

Scenario
In this post, we are taking an example to demonstrate how we can override an out of the box (OOTB) servlet in AEM. In the DAM touch UI experience interface, one or more assets can be chosen and downloaded in zip format. Let's presume that we would want to download the asset without zipping (compressing) for the case when only one asset has been chosen. This needs overriding the OOTB asset download servlet.

Step 1 – Find out the servlet which you want to override.
In our case, it’s the asset download servlet we want to override. If we track down the url of download asset feature in Dam touch UI, we could easily decipher the path, selectors, extension, and suffix. Use sling url structure as a reference to decompose your url.

http://localhost:4502/content/dam/geometrixx/offices/basel%20roof.jpg.assetdownload.zip/Basel%20View%20from%20Roof.zip?_charset_=utf-8&downloadAssets=true&downloadRenditions=false&downloadSubassets=undefined&licenseCheck=true

Path - /content/dam/geometrixx/offices/basel%20roof.jpg
Selectors - assetdownload
Extension - Zip


You could also use the sling servlet resolver test available at the AEM system console to identify the servlet that responds to the url invocation. In our example scenario of asset download, it’s the com.day.cq.dam.core.impl.servlet.AssetDownloadServlet which response to download requests. We would have to override this servlet to implement our customization.

Step 2 – Get hold of servlet specific OSGi properties of the OOTB servlet
If you’ve access to the source code, well and good. If not, use the depfinder available at the AEM system console to identify the maven dependency of the bundle that houses this servlet. With this maven coordinates, you get hold of the bundle and check the OSGi properties specific for this servlet. In the case of our AssetDownloadServlet, below are the OSGi properties.

@Component(metatype = false)
@Service
@Properties({
@Property(name = "sling.servlet.resourceTypes", value = "sling/servlet/default"),
@Property(name = "sling.servlet.methods", value = {"GET","POST"}),
@Property(name = "sling.servlet.selectors", value = "assetdownload")
})


Decoding the above, we understand that this AssetDownloadServlet responds to any requests of HTTP method GET/POST with the selector “assetdownload”. Note that this servlet is marked as default servlet using the special resourceType “sling/servlet/default”.

Step 3 – Create your custom servlet with right servlet specific OSGi properties.

@Component(metatype = false)
@Service
@Properties(value = {
@Property(name = "sling.servlet.resourceTypes", value = DamConstants.NT_DAM_ASSET),
@Property(name = "sling.servlet.methods", value = {"GET", "POST"}),
@Property(name = "sling.servlet.selectors", value = "assetdownload")
})
public class CustomAssetDownloadServlet extends SlingAllMethodsServlet implements OptingServlet {
private static final Logger log = LoggerFactory.getLogger(CustomAssetDownloadServlet.class);

protected void doGet(SlingHttpServletRequest request,
SlingHttpServletResponse response)
throws ServletException, IOException {
log.info("Custom download servlet being invoked. Path(s) requested for asset download - {}", request.getResource().getPath());
}

/**
* Examines the request, and return <code>true</code> if this servlet is
* willing to handle the request. If <code>false</code> is returned, the
* request will be ignored by this servlet, and may be handled by other
* servlets.
*
* @param request The request to examine
* @return <code>true</code> if this servlet will handle the request,
* <code>false</code> otherwise
*/
@Override
public boolean accepts(SlingHttpServletRequest request) {
if (null != request.getRequestParameter("path")) { //multiple asset download case
log.info("Custom download servlet being invoked. Multiple assets chosen and hence not accepting.");
return false;
}
return true;
}
}


Here we have the custom servlet which overrides the OOTB AssetDownloadServlet only for cases when a single asset has been chosen for download. All the servlet specific OSGi properties of OOTB servlet have been copied down to this custom servlet with one exception that sling.servlet.resourceType now pointing to dam:Asset (meaning, no more it’s the default servlet). By explicitly mentioning that this custom servlet targets these HTTP methods, the selectors and the specific resource type (dam:Asset) sling servlet resolution in SlingServletResolver prioritizes this custom servlet over the OOTB AssetDownloadServlet. In case of a download request for multiple asset selection, this custom servlet does not accept the request but passes it on to the OOTB asset download servlet by implementing the OptingServlet interface and having this check-in the accept method.

Implementation of this example in executable form is available in github. Please click here.

References
SlingServletResolver source code.
Apache sling servlet implementation details.




By aem4beginner

No comments:

Post a Comment

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