Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.
SOLVED

Custom implementation for RemoteContentRendererRequestHandler

Avatar

Level 3

Hello Members,

 

Has any one tried implementing custom usecase for RemoteContentRendererRequestHandler by following the below documentation?

 

https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/dev...

 

Below is our implementation which works fine in local SDK but not in cloud service. 

I refer to RemoteContentRendererRequestHandler defaultHandler; which is required for processing. Unfortunately this does not seem to become available and hence my custom implementation is never invoked.

 

I have also delayed the service activation with immediate=false which does not help

I have also tried so that my service gets activated and gets the reference when available which also does not help

@reference(
cardinality = ReferenceCardinality.OPTIONAL,
policy = ReferencePolicy.DYNAMIC,
policyOption = ReferencePolicyOption.GREEDY
)
private volatile RemoteContentRendererRequestHandler defaultHandler;

 

Kindly share your thoughts on what would be wrong in this implementation

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.remote.content.renderer.RemoteContentRendererRequestHandler;
import com.adobe.cq.remote.content.renderer.RemoteContentRenderingException;
import com.google.gson.JsonParser;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.sling.api.SlingHttpServletRequest;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

@Component(immediate = true,
	service = RemoteContentRendererRequestHandler.class,
	property={
		Constants.SERVICE_RANKING +":Integer=1000"
	})
public class CustomRemoteContentRendererRequestHandlerImpl implements RemoteContentRendererRequestHandler {

	private static final Logger LOGGER = LoggerFactory.getLogger(CustomRemoteContentRendererRequestHandlerImpl.class);

	public static final String HEAD_DATA = "headData";

	@Reference(
		cardinality = ReferenceCardinality.MANDATORY,
		policy = ReferencePolicy.DYNAMIC
	)
	private volatile RemoteContentRendererRequestHandler defaultHandler;

	private final ThreadLocal<SlingHttpServletRequest> currentRequest = new ThreadLocal<>();

	@Activate
	void init() {
		if (defaultHandler == null) {
			LOGGER.error("CustomRemoteContentRendererRequestHandlerImpl: Default RemoteContentRendererRequestHandler is null!");
			throw new IllegalStateException("Default RemoteContentRendererRequestHandler is not available.");
		}
		LOGGER.info("CustomRemoteContentRendererRequestHandlerImpl: initialized with defaultHandler: {}", defaultHandler);
	}

	public ThreadLocal<SlingHttpServletRequest> getCurrentRequest() {
		return currentRequest;
	}

	public void setCurrentRequest(final SlingHttpServletRequest request) {
		currentRequest.set(request);
	}

	private void clearCurrentRequest() {
		currentRequest.remove();
	}

	@Override
	public boolean canHandle(final SlingHttpServletRequest servletRequest) {
		return defaultHandler.canHandle(servletRequest);
	}

	@Override
	public RequestConfig getRequestConfig(final SlingHttpServletRequest servletRequest) {
		setCurrentRequest(servletRequest);
		return defaultHandler.getRequestConfig(servletRequest);
	}

	@Override
	public HttpUriRequest getRequest(final SlingHttpServletRequest servletRequest, final ComponentExporter componentExporter) throws RemoteContentRenderingException {
		return defaultHandler.getRequest(servletRequest, componentExporter);
	}

	/**
	 * This method grabs the response from Remote server and sets to request attribute
	 *
	 * Response from React will be in the form of
	 *
	 * { "html": "SSR constructed html", "headData": "{"attr1": "val1" ... }"}
	 *
	 * @Param httpResponse Response to the request sent to the remote endpoint
	 * @Return String response
	 * @throws IOException
	 */
	@Override
	public String getResponseMessage(final CloseableHttpResponse httpResponse) throws IOException {

		try {
			final String response = defaultHandler.getResponseMessage(httpResponse);

			final var jsonResponse = JsonParser.parseString(response).getAsJsonObject();

			final String htmlContent = jsonResponse.get("html").getAsString();

			final SlingHttpServletRequest request = currentRequest.get();
			if (request != null) {
				var headDataNode = jsonResponse.get(HEAD_DATA);
				if (headDataNode != null && !headDataNode.isJsonNull() && headDataNode.getAsJsonObject().size() > 0) {
					request.setAttribute(HEAD_DATA, headDataNode.toString());
					LOGGER.debug("CustomRemoteContentRendererRequestHandlerImpl: headData received for request : {} is : {}", request, headDataNode);
				}
			}

			return htmlContent;

		} finally {
			clearCurrentRequest();
		}

	}
}

 

1 Accepted Solution

Avatar

Correct answer by
Level 3

hi @arunpatidar 

 

Thanks for the comment. I was fortunate to fix it myself and was about to add a comment.

 

Use case is as below

We work on react spa on ssr hence we rely on RemoteContentRendererRequestHandler which does a post request to remote server to get the response built and inject to the body. This happens OOB.

However we came across a scenario where the remote server performs an API call and gets some data which needs to be updated on the <head> element of the page. Basically the SEO attributes from API

 

Hence we updated the remote server to send html and the required head attributes in json format so that i can grab the response from remote server and play around the page.

 

Solution :

Was to implement a Custom service and intercept the response from remote and update the page source

 

During this process i identified 2 issues which i could solve eventually.

1. This was happening only in cloud instance. However i had old version of SDK and could not reproduce the issue. Hence i updated to latest SDK

2. As i referenced RemoteContentRendererRequestHandler there was a cyclic dependency and the service was not brought up

@Reference(
		cardinality = ReferenceCardinality.MANDATORY,
		policy = ReferencePolicy.DYNAMIC
	)
	private volatile RemoteContentRendererRequestHandler defaultHandler;

  To solve this i had to use target attribute so that i could break the cyclic dependency

@Reference(
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
policyOption = ReferencePolicyOption.GREEDY,
target = "(!(component.name=<package.name>.CustomRemoteContentRendererRequestHandlerImpl))"
)
private volatile RemoteContentRendererRequestHandler defaultHandler;

 Thanks

Arun

View solution in original post

3 Replies

Avatar

Community Advisor

Hi @arungm20 
Could you please share the use case?

However, I have not tried implementing this. Just wanted to know what is the use case and if can be done with something else like Sling Filters or transformation pipeline.



Arun Patidar

Avatar

Correct answer by
Level 3

hi @arunpatidar 

 

Thanks for the comment. I was fortunate to fix it myself and was about to add a comment.

 

Use case is as below

We work on react spa on ssr hence we rely on RemoteContentRendererRequestHandler which does a post request to remote server to get the response built and inject to the body. This happens OOB.

However we came across a scenario where the remote server performs an API call and gets some data which needs to be updated on the <head> element of the page. Basically the SEO attributes from API

 

Hence we updated the remote server to send html and the required head attributes in json format so that i can grab the response from remote server and play around the page.

 

Solution :

Was to implement a Custom service and intercept the response from remote and update the page source

 

During this process i identified 2 issues which i could solve eventually.

1. This was happening only in cloud instance. However i had old version of SDK and could not reproduce the issue. Hence i updated to latest SDK

2. As i referenced RemoteContentRendererRequestHandler there was a cyclic dependency and the service was not brought up

@Reference(
		cardinality = ReferenceCardinality.MANDATORY,
		policy = ReferencePolicy.DYNAMIC
	)
	private volatile RemoteContentRendererRequestHandler defaultHandler;

  To solve this i had to use target attribute so that i could break the cyclic dependency

@Reference(
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
policyOption = ReferencePolicyOption.GREEDY,
target = "(!(component.name=<package.name>.CustomRemoteContentRendererRequestHandlerImpl))"
)
private volatile RemoteContentRendererRequestHandler defaultHandler;

 Thanks

Arun