Delegate fails for Custom Container Component | Community
Skip to main content
chintan1997
Level 2
January 19, 2022

Delegate fails for Custom Container Component

  • January 19, 2022
  • 3 replies
  • 7491 views

Hello,

I am writing custom container component by extending AEM core container component. I am trying to use delegation pattern for updating a method. But delegation fails with Null Pointer Exception.

 

So far, I have AEM container component with sling:resourceSuperType="core/wcm/components/container/v1/container" set and no other logic set from frontend side. I created an interface ContainerModel.java extending Container model from 

com.adobe.cq.wcm.core.components.models to have a testMethod as addition method. I created 

ContainerModelImpl.class that implements ContainerModel.

 

My @Model looks like below.

 

 

 

@Model( adaptables = SlingHttpServletRequest.class, adapters = { Container.class, ComponentExporter.class}, resourceType = ContainerModelImpl.RESOURCE_TYPE, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL )

 

 

 

 

 

 

The RESOURCE_TYPE is set as mysite/components/container pointing to container component. I am initializing delegate as below.

 

 

 

@1961677 @Via(type = ResourceSuperType.class) private Container delegate;

 

 

 

Now, all I am trying is to Override getId method that just returns delegate.getId() to test it out but it fails as well. Just a note that I am using Exporter as below.

 

 

 

@3484101( name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION )

 

 

 

All other methods follow same logic and I am delegating to parent methods.

 

I am getting below warning and then null pointer exception pointing back to delegate.getId() statement.

 

 

 

*WARN* GET /content/mysite/us/en/mysitepage.model.json HTTP/1.1] org.apache.sling.models.impl.via.ResourceSuperTypeViaProvider Could not determine forced resource type for ResourceTypeForcingResourceWrapper, type=wcm/foundation/components/responsivegrid, path=/content/mysite/us/en/mysitepage/jcr:content/root, resource=[ResourceTypeForcingResourceWrapper, type=core/wcm/components/container/v1/container, path=/content/mysite/us/en/mysitepage/jcr:content/root, resource=[JcrNodeResource, type=mysite/components/container, superType=null, path=/content/mysite/us/en/mysitepage/jcr:content/root]] using via value .

 

 

 

Am I missing some additional requirements for delegating Container component? The warning mentions about  wcm/foundation/components/responsivegrid. Do I need to configure something for that part? I am using delegation pattern for other components in same codebase with same approach but they are working fine and can initialize delegate as expected.

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.

3 replies

milind_bachani
Adobe Employee
Adobe Employee
January 19, 2022

hi @chintan1997 ,

This is one of the interesting issue and seems to have been fixed, refer :
https://github.com/adobe/aem-core-wcm-components/issues/1187

 

Can you please check the AEM version and AEM WCM Core components version being used on your instance ?

Thanks

chintan1997
Level 2
January 19, 2022

Hi @milind_bachani ,

Thanks for pointing it out. I came across same link before posting but I am currently using near latest versions of both. According to that issue, a fix was added in core components version 2.14.0 and I am currently using 2.17.12. For AEM SDK, it's 2021.11.6013.20211105T162756Z-211000. But I am still having issues. Wondering if there is anyone else who could successfully implement Container component through delegation pattern.

JeevanRaj
Community Advisor
Community Advisor
January 20, 2022

Hi Chintan,

 

The core v1 container component at /apps/core/wcm/components/container/v1/container uses the Sling model "com.adobe.cq.wcm.core.components.models.LayoutContainer". You can check this at line 16  "/apps/core/wcm/components/container/v1/container/container.html". You can use this interface as adapter in your custom sling model. Please find below the working code which overrides getId() and also i have added a testMethod().

import com.adobe.cq.wcm.core.components.models.LayoutContainer;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.via.ResourceSuperType;

@Model(adaptables = {Resource.class, SlingHttpServletRequest.class},
adapters = LayoutContainer.class, //Use LayoutContainer.class instead of Container.class
resourceType = "mysite/components/content/container",
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class CustomContainerModel implements LayoutContainer {
@Self
@Via(type = ResourceSuperType.class)
private LayoutContainer container;

@Override
public String getBackgroundStyle() {
return "black";
}

// Override getId method
@Override
public String getId() {
return "myId";
}

// adding a custom method
public String testMethod() {
return "From test method";
}
}

Below is the code inside my proxy container component.

<sly data-sly-use.container="com.adobe.cq.wcm.core.components.models.LayoutContainer"/>
<p>Container id: ${container.id}</p>
<p>Container test method: ${container.testMethod}</p>

Let me know if you have further questions. 

Thanks

chintan1997
Level 2
January 20, 2022

Hi @jeevanraj ,

Thanks for detailed solution and this solution seems to be a little relief. However, I was expecting it to return children data as well. For example, if we have text component under container component, delegating through LayoutContainer doesn't help to get that component data in model.json. To reproduce the behaviour, add text component under container and check model.json with CustomContainerModel and without any custom sling model implementation. The one without custom sling model returns children component data as well but if we implement Container through CustomContainerModel, it just returns container data and no children underneath it. 

JeevanRaj
Community Advisor
Community Advisor
January 21, 2022

Hi @chintan1997 

 

The solution to your issue is to add LayoutContainer and ComponentExporter as adapters to your CustomContainer model. You will get the child resource data as well as acess to any custom method you write in your CustomContainer model. Below is the code snippet.

 

@Model(
adaptables = { SlingHttpServletRequest.class },
adapters = { LayoutContainer.class, ComponentExporter.class },
resourceType = "mysite/components/content/container",
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
selector = ExporterConstants.SLING_MODEL_SELECTOR,
extensions = ExporterConstants.SLING_MODEL_EXTENSION,
options = {
@ExporterOption(name = "MapperFeature.SORT_PROPERTIES_ALPHABETICALLY", value = "true"),
@ExporterOption(name = "SerializationFeature.WRITE_DATES_AS_TIMESTAMPS", value="false")
}
)
@JsonSerialize(as = CustomContainerModel.class)

public class CustomContainerModel implements ComponentExporter, LayoutContainer {

@Self
@Via(type = ResourceSuperType.class)
private LayoutContainer container;


@Override
public Map<String, ? extends ComponentExporter> getExportedItems() {
return container.getExportedItems();
}

public String getTestMethod() {
return " From SampleComponentExporter Model ";
}
}

 Below is a snapshot of the model.json

'text' and 'textimage' are the components added in the container. 'testMethod' is the method i have addedion the CustomContainer model. Let me know if this satisfies your need.

Thanks

Vijayalakshmi_S
Level 10
January 26, 2022

Hi @chintan1997 

  • We can make use of lombok's Delegate to delegate all public methods of desired Type (in this case LayoutContainer) along with its Super Types 
    • Here is the hierarchy of interface which LayoutContainer extends 
      • LayoutContainer -> Container -> Component(in turn extends ComponentExporter) & ContainerExporter
    • and with excludes attribute you can define the methods we wish to override. 
@Delegate(types = LayoutContainer.class, excludes = ContainerExcludes.class)
@Self
@Via(type = ResourceSuperType.class)
private LayoutContainer layoutContainer;

private interface ContainerExcludes {
String getId();
String getExportedType();
}
  • Other than the methods we wish to override,
    • we need to explicitly override getExportedType() of ComponentExporter (that returns our proxy container resourceType) +
    • add ComponentExporter to adapters  to export this Custom Sling Model (for it to appear in getExportedItems() / ":items" property of the JSON response)

Look for all the public getter methods in the hierarchy above, if you wish to not export any of those, override the same and annotate it with @JsonIgnore.

 

Lombok in Sling Model Delegation:

https://www.initialyze.com/blog/2020/11/simplify-extending-sling-models-with-lombok/

Sample snippet (that override getId() method) added to my GitHub - CustomContainerModel.java

chintan1997
Level 2
January 26, 2022

Hi @vijayalakshmi_s ,

Thanks for detailed answer and providing sample code snippet. As I mentioned in @jeevanraj 's answer thread, the child container data populates ":items" as empty once CustomContainer class is introduced. I tried having CustomContainerModel.class from the sample project and still having same issue.

Thanks

Vijayalakshmi_S
Level 10
January 26, 2022

@chintan1997 

Are you providing custom implementation for getExportedItems() method ?

Is it possible for you to share the complete Java class file for further debugging