Hello Members,
Has any one tried implementing custom usecase for RemoteContentRendererRequestHandler by following the below documentation?
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();
}
}
}
Solved! Go to Solution.
Views
Replies
Total Likes
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
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.
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