aem-core-wcm-components: Infinite loop using @ChildResource on a proxy extension model
Bug Report
Current Behavior
If I have model class A that has a @ChildResource
of model class B, where B is a proxy extension of a WCM Core model, the result is an infinite loop attempting to inject the child resource B into model A.
Expected behavior/code
@ChildResource
should work as expected, successfully injecting the child resource into a sling model regardless of whether it is a proxy extension of a WCM Core model
Environment
- AEM version and patch level: AEM 6.3 SP3
- Core Components version: 2.1.0
Possible Solution Export model implementation classes so that WCM Core models can be extended w/o using the proxy pattern, which requires arbitrary coding to implement all interface methods (those implementations would be able to be directly inherited) and causes infinite loops like this as well as #239.
Additional context Here are my model classes to reproduce the error.
Model Class “B” - a proxy extension of the WCM Core v2 Image component
package com.hs2solutions.aem.base.core.models;
import aQute.bnd.annotation.ConsumerType;
import com.adobe.cq.wcm.core.components.models.Image;
/**
* Defines the {@code HS2Image} Sling Model used for the {@code hs2-aem-base/components/content/image} component.
*/
@ConsumerType
public interface HS2Image extends Image {
}
...
package com.hs2solutions.aem.base.core.models.impl;
import com.adobe.cq.wcm.core.components.models.Image;
import com.adobe.cq.wcm.core.components.models.ImageArea;
import com.hs2solutions.aem.base.core.models.HS2Image;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
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.ForcedResourceType;
import java.util.List;
@Model(
adaptables = {Resource.class, SlingHttpServletRequest.class},
adapters = {Image.class, HS2Image.class},
resourceType = "hs2-aem-base/components/content/image"
)
public class HS2ImageImpl extends BaseModel implements HS2Image {
@Self
@Via(type = ForcedResourceType.class, value = "core/wcm/components/image/v2/image")
private Image superTypeImage;
@Override
public String getSrc() {
return superTypeImage.getSrc();
}
@Override
public String getAlt() {
return superTypeImage.getAlt();
}
@Override
public String getTitle() {
return superTypeImage.getTitle();
}
@Override
public String getLink() {
return superTypeImage.getLink();
}
@Override
public boolean displayPopupTitle() {
return superTypeImage.displayPopupTitle();
}
@Override
public String getFileReference() {
return superTypeImage.getFileReference();
}
@Deprecated
@Override
public String getJson() {
return superTypeImage.getJson();
}
@Override
public int[] getWidths() {
return superTypeImage.getWidths();
}
@Override
public String getSrcUriTemplate() {
return superTypeImage.getSrcUriTemplate();
}
@Override
public boolean isLazyEnabled() {
return superTypeImage.isLazyEnabled();
}
@Override
public String getExportedType() {
return superTypeImage.getExportedType();
}
@Override
public List<ImageArea> getAreas() {
return superTypeImage.getAreas();
}
}
Model class “A” - contains a reference to model class “B”
package com.hs2solutions.aem.base.core.models;
import aQute.bnd.annotation.ConsumerType;
/**
* Defines the {@code HeroBanner} Sling Model used for the {@code hs2-aem-base/components/content/hero-banner} component.
*/
@ConsumerType
public interface HeroBanner {
String getBackgroundSrc();
}
...
package com.hs2solutions.aem.base.core.models.impl;
import com.hs2solutions.aem.base.core.models.HS2Image;
import com.hs2solutions.aem.base.core.models.HeroBanner;
import com.hs2solutions.aem.base.core.models.annotations.via.OverriddenRequestPath;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.ChildResource;
import org.apache.sling.models.annotations.injectorspecific.Self;
import javax.annotation.PostConstruct;
@Model(
adaptables = {Resource.class, SlingHttpServletRequest.class},
adapters = HeroBanner.class,
resourceType = "hs2-aem-base/components/content/hero-banner"
)
public class HeroBannerImpl extends BaseModel implements HeroBanner {
@ChildResource
private HS2Image image;
@Override
public String getBackgroundSrc() {
return image != null ? image.getSrc() : null;
}
}
Sample of the stack trace:
18.02.2019 13:37:48.001 *WARN* [0:0:0:0:0:0:0:1 [1550518666253] GET /content/brand-2/language-masters/en.html HTTP/1.1] org.apache.sling.models.impl.ModelAdapterFactory Could not adapt to model
org.apache.sling.models.factory.ModelClassException: Adapting JcrNodeResource, type=hs2-aem-base/components/content/image, superType=null, path=/content/brand-2/language-masters/en/jcr:content/root/main-par/hero_banner/image to interface com.adobe.cq.wcm.core.components.models.Image failed, too much recursive invocations (>=20).
at org.apache.sling.models.impl.ModelAdapterFactory.internalCreateModel(ModelAdapterFactory.java:335)
at org.apache.sling.models.impl.ModelAdapterFactory.getAdapter(ModelAdapterFactory.java:249)
at org.apache.sling.adapter.internal.AdapterManagerImpl.getAdapter(AdapterManagerImpl.java:147)
at org.apache.sling.api.adapter.SlingAdaptable.adaptTo(SlingAdaptable.java:104)
at org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResource.adaptTo(JcrNodeResource.java:178)
at org.apache.sling.api.resource.ResourceWrapper.adaptTo(ResourceWrapper.java:180)
at org.apache.sling.models.impl.ModelAdapterFactory.adapt(ModelAdapterFactory.java:991)
at org.apache.sling.models.impl.ModelAdapterFactory.adaptIfNecessary(ModelAdapterFactory.java:963)
at org.apache.sling.models.impl.ModelAdapterFactory.setField(ModelAdapterFactory.java:906)
at org.apache.sling.models.impl.ModelAdapterFactory.access$100(ModelAdapterFactory.java:130)
at org.apache.sling.models.impl.ModelAdapterFactory$SetFieldCallback.inject(ModelAdapterFactory.java:446)
at org.apache.sling.models.impl.ModelAdapterFactory.injectElement(ModelAdapterFactory.java:541)
at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:682)
at org.apache.sling.models.impl.ModelAdapterFactory.internalCreateModel(ModelAdapterFactory.java:394)
at org.apache.sling.models.impl.ModelAdapterFactory.getAdapter(ModelAdapterFactory.java:249)
at org.apache.sling.adapter.internal.AdapterManagerImpl.getAdapter(AdapterManagerImpl.java:147)
at org.apache.sling.api.adapter.SlingAdaptable.adaptTo(SlingAdaptable.java:104)
at org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResource.adaptTo(JcrNodeResource.java:178)
at org.apache.sling.api.resource.ResourceWrapper.adaptTo(ResourceWrapper.java:180)
at org.apache.sling.models.impl.ModelAdapterFactory.adapt(ModelAdapterFactory.java:991)
at org.apache.sling.models.impl.ModelAdapterFactory.adaptIfNecessary(ModelAdapterFactory.java:963)
at org.apache.sling.models.impl.ModelAdapterFactory.setField(ModelAdapterFactory.java:906)
at org.apache.sling.models.impl.ModelAdapterFactory.access$100(ModelAdapterFactory.java:130)
at org.apache.sling.models.impl.ModelAdapterFactory$SetFieldCallback.inject(ModelAdapterFactory.java:446)
at org.apache.sling.models.impl.ModelAdapterFactory.injectElement(ModelAdapterFactory.java:541)
at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:682)
at org.apache.sling.models.impl.ModelAdapterFactory.internalCreateModel(ModelAdapterFactory.java:394)
at org.apache.sling.models.impl.ModelAdapterFactory.getAdapter(ModelAdapterFactory.java:249)
at org.apache.sling.adapter.internal.AdapterManagerImpl.getAdapter(AdapterManagerImpl.java:147)
at org.apache.sling.api.adapter.SlingAdaptable.adaptTo(SlingAdaptable.java:104)
at org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResource.adaptTo(JcrNodeResource.java:178)
... (and on and on)
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 23 (11 by maintainers)
Upside: Impl classes stay implementation detail, only API needs to be exported. Clearer/smaller API. If the whole impl models would be exported maintaining backwards compatibility would be much harder.