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)

Most upvoted comments

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.