April 1, 2020
Estimated Post Reading Time ~

How to create custom renderer for a file type in AEM

Use Case:
Honestly use cases for this could be different based on what you want to change in response for a specific file type. We will take an example where we want to add some custom header for a PDF file based on what author has added in property of that file. For example here if author has added a canonical url and no index property, then we want to add these property in response header of all pdf file request.

Prerequisite:
Note that in order to make these property available for asset, you have to override asset editor as well. One example of adding extra property to asset editor is here http://www.wemblog.com/2013/01/how-to-associate-cug-with-dam-asset-in.html

Implementation:
Key here is OptingServlet https://sling.apache.org/apidocs/sling7/org/apache/sling/api/servlets/OptingServlet.html which get invoked on every request. Here is one example

package com.wemblog.cq.core.servlet;

import com.day.cq.dam.api.Asset;
import com.day.cq.dam.commons.util.DamUtil;
import com.wemblog.utility.RunModeUtil;
import java.io.IOException;
import java.util.Dictionary;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
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.OptingServlet;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* Servlet to add extra header to PDF file request based on authored property
* @author Yogesh Upadhyay
*
*/

@Component(metatype = true)
@Service
@Properties({ @Property(name = "sling.servlet.resourceTypes", value = "nt:file", propertyPrivate = true), @Property(
name = "sling.servlet.methods", value = { "GET" }, propertyPrivate = true) })
public class CustomPDFRendererServlet extends SlingSafeMethodsServlet implements OptingServlet {

private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(CustomPDFRendererServlet.class);

//This is custom RunMode util. You might have write this. Should be simple
//Let me know if you need any help. Here is one example http://wemcode.wemblog.com/check-run-mode
//Or http://programtalk.com/vs/acs-aem-commons/bundle/src/main/java/com/adobe/acs/commons/util/ModeUtil.java/
@Reference
RunModeUtil runModeUtil;

@Property(label = "disable.customPDFRenderor", description = "Disable Custom PDF Renderor", boolValue = false)
private static final String DISABLE_PDF_RENDEROR = "disable.customPDFRenderor";
private boolean _disableCustomPdfRenderor;

private static final String PROPERTY_REL_CANONICAL_URL = "pdf:canonicalUrl";
private static final String PROPERTY_NO_INDEX = "pdf:noIndex";
private static final String HEADER_DISPATCHER_NAME = "Dispatcher";
private static final String HEADER_DISPATCHER_VALUE = "no-cache";
private static final String PDF_CUSTOM_REQUEST_ATTRIBUTE = "PDF_CUSTOM_HEADER_SET";
private static final String HEADER_NO_INDEX_NAME = "X-Robots-Tag";
private static final String HEADER_NO_INDEX_VALUE = "noindex";
private static final String HEADER_REL_CANONICAL = "Link";

@Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException,
IOException {
final Resource resource = request.getResource();
final RequestDispatcher crd = request.getRequestDispatcher(resource);
//request attribute is required to avoid infinite redirect
request.setAttribute(PDF_CUSTOM_REQUEST_ATTRIBUTE, "true");
if (resource != null) {
LOGGER.debug("Resource Path is {}", resource.getPath());
//Get Asset path based on internal asset path
final Asset asset = DamUtil.resolveToAsset(resource);
if (asset != null) {
LOGGER.debug("This is PDF Asset path is {}", asset.getPath());
boolean isDispatcherCache = true;
if (getNoIndexHeaderString(asset) != null) {
response.setHeader(HEADER_NO_INDEX_NAME, HEADER_NO_INDEX_VALUE);
isDispatcherCache = false;
}
if (getRelCanonicalHeaderString(asset) != null) {
response.setHeader(HEADER_REL_CANONICAL, getRelCanonicalHeaderString(asset));
isDispatcherCache = false;
}
//If custom property is set do not cache this document in dispatcher
if (!isDispatcherCache) {
response.setHeader(HEADER_DISPATCHER_NAME, HEADER_DISPATCHER_VALUE);
crd.include(request, response);
return;
}
}
}
//For all other PDF request just forward to next handler
crd.forward(request, response);

}

/**
* Check if No index is enabled for this document
* @param asset
* @return
*/
private boolean isNoIndexEnabled(Asset asset) {
if (StringUtils.isNotBlank(asset.getMetadataValue(PROPERTY_NO_INDEX))) {
LOGGER.debug("No index is set for document {}", asset.getPath());
return Boolean.parseBoolean(asset.getMetadataValue(PROPERTY_NO_INDEX));
}
return false;
}

/**
* Get rel canonical property from document
* @param asset
* @return
*/
private String getRelCanonicalProperty(Asset asset) {
if (StringUtils.isNotBlank(asset.getMetadataValue(PROPERTY_REL_CANONICAL_URL))) {
LOGGER.debug("Custom rel Canonical set for document {}", asset.getPath());
return asset.getMetadataValue(PROPERTY_REL_CANONICAL_URL);
}
return null;
}

/**
* Get Header String based on property
* Example: https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
* @param asset
* @return
*/
private String getNoIndexHeaderString(Asset asset) {
if (isNoIndexEnabled(asset)) {
return HEADER_NO_INDEX_VALUE;
}
return null;
}

/**
* Get custom rel canonical string
* Example: http://moz.com/blog/how-to-advanced-relcanonical-http-headers
* @param asset
* @return
*/
private String getRelCanonicalHeaderString(Asset asset) {
if (null != getRelCanonicalProperty(asset)) {
StringBuilder builder = new StringBuilder();
builder.append("<");
builder.append(getRelCanonicalProperty(asset));
builder.append(">; ");
builder.append("rel=\"canonical\"");
return builder.toString();

}
return null;
}

/**
* Check if this is PDF request
* @param request
* @return
*/
private boolean isPDFRequest(SlingHttpServletRequest request) {
return request != null && request.getRequestURI() != null ? request.getRequestURI().endsWith("pdf") : false;
}

/**
* Check if this servlet is already called
* @param request
* @return
*/
private boolean isCustomPDFHeaderSet(SlingHttpServletRequest request) {
return request.getAttribute(PDF_CUSTOM_REQUEST_ATTRIBUTE) != null;
}

/**
* Method to decide whether given request will be handled by this servlet
*/
@Override
public boolean accepts(SlingHttpServletRequest request) {
return runModeUtil.isPublish() && isPDFRequest(request) && !isCustomPDFHeaderSet(request)
&& !this._disableCustomPdfRenderor;
}

@Activate
protected final void activate(final ComponentContext componentContext) {
final Dictionary<?, ?> config = componentContext.getProperties();
this._disableCustomPdfRenderor = PropertiesUtil.toBoolean(config.get(DISABLE_PDF_RENDEROR), false);
}

}


By aem4beginner

No comments:

Post a Comment

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