Expand my Community achievements bar.

Resolving custom type from a SyntheticResource

Avatar

Level 3

Hi AdobeTeam,

I have created
- a custom resource type which is inherited from the core resource type "core/wcm/components/navigation/v2/navigation",
- the respective Sling model exporter which contains the following annotation:

     @Self@Via(type = ResourceSuperType.class
     private Navigation navigation;

I would like to get the json Sling model for my custom resource generated by calling it from a SlingAllMethodsServlet which gets the navigation attributes in query parameters. To do that I created a SyntheticResource (i.e. ValueMapResource) where I pass some Navigation parameters for the model.

Doing it like this:
...

ValueMap properties = new ValueMapDecorator(new HashMap<>());
properties.put(PN_NAVIGATION_ROOT, navigationRoot);
properties.put(PN_STRUCTURE_START, structureStart);
ValueMapResource syntheticResource = new ValueMapResource(resourceResolver, "/syntheticResourcePath", RESOURCE_TYPE, properties);
UeNavigationModel model = modelFactory.getModelFromWrappedRequest(request, syntheticResource, UeNavigationModel.class);
...

The correct model is found and created by the factory but the 
@Self @Via(type = ResourceSuperType.class) private Navigation navigation
instance variable in the model is null.

I debugged the resolution of the model for a long time and seems that although the correct core Adobe Navigation implementation is found but some variables in it can not be resolved, i.e.:
 
/**
* The current page.
*/
@ScriptVariable
private Page currentPage;

/**
* The current style.
*/
@ScriptVariable
private Style currentStyle;

These missing variables in the parent core Navigation model must cause the @Self Navigation variable to be null. 

Can you help me what I should set in my ValueMapResource / request so that the currentPage and currentStyle variables can be resolved in the SlingBindings (that's where those variables are contained but holding null value)? 

Thanks,
Peter

 

4 Replies

Avatar

Community Advisor

Hi @pnagy ,

To resolve the currentPage and currentStyle variables in your custom model when using a SyntheticResource, you need to ensure that these variables are set correctly in the Sling bindings. These variables are typically set by the Sling scripting context when rendering a page in AEM, but when you create a synthetic resource programmatically, you need to set them manually.

Here's how you can achieve this:

  1. Set the Script Variables in the Request: Add the currentPage and currentStyle variables to the request attributes or the Sling bindings before creating the model.

  2. Use SlingBindings to set variables: You can use the SlingBindings class to set the necessary variables.

Step-by-Step Solution

  1. Create and set up the SyntheticResource:

 

ValueMap properties = new ValueMapDecorator(new HashMap<>());
properties.put(PN_NAVIGATION_ROOT, navigationRoot);
properties.put(PN_STRUCTURE_START, structureStart);
Resource syntheticResource = new ValueMapResource(resourceResolver, "/syntheticResourcePath", RESOURCE_TYPE, properties);

 

Set up the currentPage and currentStyle variables:

 

Page currentPage = resourceResolver.adaptTo(PageManager.class).getPage("/path/to/current/page");
Style currentStyle = currentPage.getContentResource().adaptTo(Style.class);

 

Set these variables in the SlingBindings and the request:

 

SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName());
if (bindings == null) {
    bindings = new SlingBindings();
    request.setAttribute(SlingBindings.class.getName(), bindings);
}
bindings.put("currentPage", currentPage);
bindings.put("currentStyle", currentStyle);

 

Retrieve the model using the ModelFactory:

 

UeNavigationModel model = modelFactory.getModelFromWrappedRequest(request, syntheticResource, UeNavigationModel.class);

 

Example Code

Here's how all these pieces fit together:

 

import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.resource.ValueMapDecorator;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.factory.ModelFactory;
import com.adobe.cq.sightly.WCMUsePojo;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.wcm.api.designer.Style;
import org.apache.sling.models.annotations.via.ResourceSuperType;
import org.apache.sling.api.scripting.SlingBindings;
import java.util.HashMap;

// In your SlingAllMethodsServlet
public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
    ResourceResolver resourceResolver = request.getResourceResolver();

    ValueMap properties = new ValueMapDecorator(new HashMap<>());
    properties.put(PN_NAVIGATION_ROOT, navigationRoot);
    properties.put(PN_STRUCTURE_START, structureStart);
    Resource syntheticResource = new ValueMapResource(resourceResolver, "/syntheticResourcePath", RESOURCE_TYPE, properties);

    Page currentPage = resourceResolver.adaptTo(PageManager.class).getPage("/path/to/current/page");
    Style currentStyle = currentPage.getContentResource().adaptTo(Style.class);

    SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName());
    if (bindings == null) {
        bindings = new SlingBindings();
        request.setAttribute(SlingBindings.class.getName(), bindings);
    }
    bindings.put("currentPage", currentPage);
    bindings.put("currentStyle", currentStyle);

    UeNavigationModel model = modelFactory.getModelFromWrappedRequest(request, syntheticResource, UeNavigationModel.class);
    
    // Use the model as needed
}

 

Explanation

  • SlingBindings: By creating or retrieving the SlingBindings object from the request and setting the currentPage and currentStyle attributes, you ensure that these variables are available to your Sling model.

  • ResourceSuperType: The @Via(type = ResourceSuperType.class) annotation correctly resolves the parent model (Navigation in this case) and sets up its dependencies if they are available in the Sling bindings or request attributes.

This setup ensures that when the model is instantiated, it can access the currentPage and currentStyle variables, avoiding the null issue in the parent Navigation model.

Avatar

Level 3

Thanks a lot @HrishikeshKa  for the detailed suggestion, this is the same approach I tried TBH.

The issue is that the request will be wrapped into a ResourceOverridingRequestWrapper before resolving to a model. That wrapped request has this logic:


ResourceOverridingRequestWrapper
(SlingHttpServletRequest wrappedRequest, Resource resource,
AdapterManager adapterManager, SlingModelsScriptEngineFactory scriptEngineFactory,
BindingsValuesProvidersByContext bindingsValuesProvidersByContext) {
super(wrappedRequest);
this.resource = resource;
this.adapterManager = adapterManager;

SlingBindings existingBindings = (SlingBindings) wrappedRequest.getAttribute(SlingBindings.class.getName());

bindings = new SlingBindings();
if (existingBindings != null) {
bindings.put(SLING, existingBindings.getSling());
bindings.put(RESPONSE, existingBindings.getResponse());
bindings.put(READER, existingBindings.getReader());
bindings.put(OUT, existingBindings.getOut());
bindings.put(LOG, existingBindings.getLog());
}

So even though I have the "existing" slingBinding in my original request but only some of them will be put into the wrapped request and currentStyle / currentPage will be lost.

So I think this approach does not work, probably I should set some other properties but unsure what are those.

Thanks and regards,
Peter

Avatar

Level 3

I think I have the good question which I don't know the answer yet for:
what s the prerequisite for com.day.cq.wcm.scripting.impl.WCMBindingsValuesProvider so that it can calculate "currentStyle" for in the SlingBindings?

Avatar

Level 3

Can someone help with finding the source / jar for com.day.cq.wcm.scripting.impl.WCMBindingsValuesProvider ?

The "currentPage" property will be set in SlingBindings when I set a page resource path for the 
ValueMapResource but the currentStyle is yet empty. I would like to understand what is missing to get it calculated.