Hi All,
I am using a Sling model exporter to export json data in AEM 6.5 and using the selector "caas". When the selector is "model" , it works perfectly. However with custom selector , it shows the below error.
Cannot serve request to /content/abc/internal/exportsite/en/clientaccess/help/secondlevela/level3.caas.json in org.apache.sling.servlets.get.DefaultGetServlet
Already tried the below:
check if this helps - Re: Sling Exporter with custom selector
Views
Replies
Total Likes
Is /content/abc/internal/exportsite/en/clientaccess/help/secondlevela/level3 a page?
Did you try with /content/abc/internal/exportsite/en/clientaccess/help/secondlevela/level3/jcr:content.caas.json?
Thanks!
I am getting blank page with the above link also, only getting response from model.json
Thanks
Can you share the Sling Model here please?
@Model(adaptables = SlingHttpServletRequest.class, adapters = { PageExporter.class,
ComponentExporter.class }, resourceType = PageExporterImpl.RESOURCE_TYPE)
@exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,selector = "caas", extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class PageExporterImpl extends com.adobe.cq.wcm.core.components.internal.models.v1.PageImpl implements PageExporter{
protected static final String RESOURCE_TYPE = "contentservices/components/structure/page";
/** The logger. */
private final Logger logger = LoggerFactory.getLogger(PageExporterImpl.class);
@ScriptVariable
protected com.day.cq.wcm.api.Page currentPage;
private Map<String, ComponentExporter> childModels = null;
protected String title;
protected String description;
@Override
public String getTitle() {
return "title";
}
@Override
public String getDescription() {
return description;
}
@Override
public String getLanguage() {
return currentPage == null ? Locale.getDefault().toLanguageTag()
: currentPage.getLanguage(false).toLanguageTag();
}
@PostConstruct
@Override
protected void initModel() {
title = currentPage.getTitle();
description = currentPage.getDescription();
}
@Override
@JsonIgnore
public String[] getExportedItemsOrder() {
return super.getExportedItemsOrder();
}
@Override
public Map<String, ? extends ComponentExporter> getExportedItemsString() {
String response = "";
Map<String, ? extends ComponentExporter> componentMap = (Map<String, ? extends ComponentExporter>) super.getExportedItems();
Set<String> childKeyList = super.getExportedItems().keySet();
for (String childKey : childKeyList) {
ResponsiveGrid res= (ResponsiveGrid) componentMap.get(childKey);
Map<String, ? extends ComponentExporter> resMap = res.getExportedItems();
return resMap;
}
return null;
}
@JsonIgnore
@Override
public Map<String, ? extends ComponentExporter> getExportedItems() {
return super.getExportedItems();
}
@Override
@JsonIgnore
public String getExportedType() {
// TODO Auto-generated method stub
return null;
}
}
Can you try with the below and confirm?
@Model(adaptables = Resource.class, resourceType="/apps/contentservices/components/structure/page", defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@exporter(name = "jackson", selector="caas", extensions = "json", options = { @ExporterOption(name = "SerializationFeature.WRITE_DATES_AS_TIMESTAMPS", value = "true") })
Getting the same error.
Thanks
Hi @vikrams57194913,
Per the snippet you have shared, below highlighted in bold is the issue. This is OOTB Core Page Implementation which is internal and not allowed to use as is directly. (Bundle should have been in installed state)
If you are to use its method implementation or override, use Sling model delegation pattern.
public class PageExporterImpl extends com.adobe.cq.wcm.core.components.internal.models.v1.PageImpl implements PageExporter{
No, that's not the issue, I have already tried that also. But getting the same error.
@Model(adaptables = SlingHttpServletRequest.class, adapters = { PageExporter.class,
ComponentExporter.class }, resourceType = PageExporterImpl.RESOURCE_TYPE)
@exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,selector="mobile",extensions = ExporterConstants.SLING_MODEL_EXTENSION)
//@Model(adaptables = Resource.class, resourceType="/apps/contentservices/components/structure/page", defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
//
//@Exporter(name = "jackson", selector="caas", extensions = "json", options = { @ExporterOption(name = "SerializationFeature.WRITE_DATES_AS_TIMESTAMPS", value = "true") })
public class PageExporterImpl implements PageExporter{
protected static final String RESOURCE_TYPE = "contentservices/components/structure/page";
/** The logger. */
private final Logger logger = LoggerFactory.getLogger(PageExporterImpl.class);
/** The sling http servlet request. */
@Deleted Account(injectionStrategy = InjectionStrategy.REQUIRED)
private SlingHttpServletRequest slingHttpServletRequest;
@ScriptVariable
protected com.day.cq.wcm.api.Page currentPage;
private Map<String, ComponentExporter> childModels = null;
protected String title;
protected String description;
@Override
public String getTitle() {
return "title";
}
@Override
public String getDescription() {
return description;
}
@Override
public String getLanguage() {
return currentPage == null ? Locale.getDefault().toLanguageTag()
: currentPage.getLanguage(false).toLanguageTag();
}
@PostConstruct
protected void initModel() {
title = currentPage.getTitle();
description = currentPage.getDescription();
}
@Override
@JsonIgnore
public String getExportedType() {
return null;
}
}
Is it possible for you to share the details about your POC. (Use case that you are trying to achieve)
And about this resource - contentservices/components/structure/page. Is this in turn inheriting from any other Page resource. If yes, share the complete resource hierarchy
Actually, we are planning to use the exporter at two level, one is page level and second is component level, but we want to run these exporter on the basis of selector.
contentservices/components/structure/page - It's resource super type is core/wcm/components/page/v2/page
Please let me know if you need any further details.
Thanks
Thanks for the inputs @vikrams57194913
Can you share the complete Java package name of PageExporter that you are implementing from.
Sure, please find the interface PageExporter code below:
@ConsumerType
public interface PageExporter extends ComponentExporter{
default String getLanguage() {
return null;
}
default String getTitle() {
return null;
}
default String getDescription() {
return null;
}
}
Thanks for sharing @vikrams57194913 .
Have a backup of your model class separately and use the below (have used the PageExporter interface similar to yours), see if it works.
getMessage is just added for testing if it is from this impl (Its not part of the interface)
package com.aem.demoproject.core.models; import com.adobe.cq.export.json.ComponentExporter; import com.adobe.cq.export.json.ExporterConstants; import com.day.cq.wcm.api.Page; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.Exporter; import org.apache.sling.models.annotations.Model; import javax.annotation.Nonnull; import javax.inject.Inject; @Model(adaptables = SlingHttpServletRequest.class, adapters = {PageExporter.class, ComponentExporter.class}, resourceType = PageExporterImpl.RESOURCE_TYPE) @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, selector = "mobile", extensions = ExporterConstants.SLING_MODEL_EXTENSION) public class PageExporterImpl implements PageExporter { protected static final String RESOURCE_TYPE = "demoproject/components/structure/page"; @Inject private Page currentPage; @Nonnull @Override public String getExportedType() { return RESOURCE_TYPE; } @Override public String getLanguage() { return "en"; } @Override public String getTitle() { return currentPage != null ? currentPage.getTitle() : "Default Title"; } @Override public String getDescription() { return currentPage != null ? currentPage.getDescription() : "Default Desc"; } public String getMessage(){ return "Custom Page Exporter Impl"; } }
Result :
Thanks now its work. But now when I am using Sling model delegation pattern for retrieving page get exported items I am getting Null pointer exception.
@Model(adaptables = SlingHttpServletRequest.class, adapters = { PageExporter.class,
ComponentExporter.class, Page.class }, resourceType = PageExporterImpl.RESOURCE_TYPE, defaultInjectionStrategy=DefaultInjectionStrategy.OPTIONAL)
@exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,selector = "mobile",extensions = ExporterConstants.SLING_MODEL_EXTENSION)
//@Model(adaptables = Resource.class, resourceType="/apps/contentservices/components/structure/page", defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
//
//@Exporter(name = "jackson", selector="caas", extensions = "json", options = { @ExporterOption(name = "SerializationFeature.WRITE_DATES_AS_TIMESTAMPS", value = "true") })
public class PageExporterImpl implements PageExporter, Page{
protected static final String RESOURCE_TYPE = "contentservices/components/structure/page";
/** The logger. */
private final Logger logger = LoggerFactory.getLogger(PageExporterImpl.class);
/** The sling http servlet request. */
@Deleted Account(injectionStrategy = InjectionStrategy.REQUIRED)
private SlingHttpServletRequest slingHttpServletRequest;
@ScriptVariable
protected com.day.cq.wcm.api.Page currentPage;
private Map<String, ComponentExporter> childModels = null;
protected String title;
protected String description;
@Deleted Account @Via(type = ResourceSuperType.class)
private Page page;
@Override
public String getTitle() {
return "title";
}
@Override
public String getDescription() {
return description;
}
@Override
public String getLanguage() {
return currentPage == null ? Locale.getDefault().toLanguageTag()
: currentPage.getLanguage(false).toLanguageTag();
}
@PostConstruct
protected void initModel() {
title = currentPage.getTitle();
description = currentPage.getDescription();
}
@Override
public Map<String, ? extends ComponentExporter> getExportedItems() {
return page.getExportedItems();
}
@Override
@JsonIgnore
public String getExportedType() {
// TODO Auto-generated method stub
return null;
}
}
We need to provide implementation for all methods and delegate. We can make use of lombok @Delegate annotation for this - https://www.initialyze.com/blog/2020/11/simplify-extending-sling-models-with-lombok/
Also, note that your custom interface(PageExporter) has methods in the same name as OOTB core Page interface. Cross check the same and rename/remove based on your need.
You can use the below and see if it works.
package com.aem.demoproject.core.models; import com.adobe.cq.export.json.ComponentExporter; import com.adobe.cq.export.json.ExporterConstants; import com.day.cq.wcm.api.Page; import lombok.experimental.Delegate; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.Exporter; 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; import javax.inject.Inject; import java.util.Map; @Model(adaptables = SlingHttpServletRequest.class, adapters = {com.adobe.cq.wcm.core.components.models.Page.class}, resourceType = PageExporterImpl.RESOURCE_TYPE) @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, selector = "mobile", extensions = ExporterConstants.SLING_MODEL_EXTENSION) public class PageExporterImpl implements com.adobe.cq.wcm.core.components.models.Page { protected static final String RESOURCE_TYPE = "demoproject/components/structure/page"; @Inject private Page currentPage; @Delegate(types = com.adobe.cq.wcm.core.components.models.Page.class, excludes = Handled.class) @Self @Via(type = ResourceSuperType.class) private com.adobe.cq.wcm.core.components.models.Page pageDelegate; @Override public Map<String, ? extends ComponentExporter> getExportedItems() { return pageDelegate.getExportedItems(); } public String getMessage() { return "Custom Page Exporter Impl"; } protected static interface Handled { public Map<String, ? extends ComponentExporter> getExportedItems(); } }
Result :
Getting below error with the above code:
<h1>Error during include of component
'/apps/contentservices/components/structure/page'</h1><h3>Error Message:</h3> <pre>org.apache.sling.models.factory.MissingElementsException:
Could not inject all required fields into class
ca.demo.web.contentservices.core.impl.PageExporterImpl</pre><h3>Processing Info:</h3> <table style='font-family: monospace'> <tr><td>Page</td><td>=</td><td>
/content/demo/internal/exportsite/en/clientaccess/help/secondlevela/level3<td></tr><tr>
<td>Resource Path</td><td>=</td><td>/content/demo/internal/exportsite/en/clientaccess/help
/secondlevela/level3/jcr:content<td></tr><tr><td>Cell</td><td>=</td><td>page<td></tr><tr>
<td>Cell Search Path</td><td>=</td><td>page|basicpage<td></tr><tr><td>Component Path</td>
<td>=</td><td>/apps/contentservices/components/structure/page<td></tr></table> <h3>Sling Request Progress:</h3>
I also applied defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
in the model, after that getting Null pointer exception on language etc.
Thanks
Please share your model class - PageExporterImpl (that introduced this exception)
package ca.sunlife.web.contentservices.core.impl;
import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.day.cq.wcm.api.NameConstants;
import com.day.cq.wcm.api.Page;
import lombok.experimental.Delegate;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.via.ResourceSuperType;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.Calendar;
import java.util.Locale;
import java.util.Map;
@Model(adaptables = SlingHttpServletRequest.class,defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL, adapters = {com.adobe.cq.wcm.core.components.models.Page.class}, resourceType = PageExporterImpl.RESOURCE_TYPE)
@exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, selector = "mobile", extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class PageExporterImpl implements com.adobe.cq.wcm.core.components.models.Page {
protected static final String RESOURCE_TYPE = "contentservices/components/structure/page";
@Delegate(types = com.adobe.cq.wcm.core.components.models.Page.class, excludes = Handled.class)
@Deleted Account
@Via(type = ResourceSuperType.class)
private com.adobe.cq.wcm.core.components.models.Page pageDelegate;
@Override
public Map<String, ? extends ComponentExporter> getExportedItems() {
return pageDelegate.getExportedItems();
}
public String getMessage() {
return "Custom Page Exporter Impl";
}
protected static interface Handled {
public Map<String, ? extends ComponentExporter> getExportedItems();
}
}
But below code works for me
@Model(adaptables = SlingHttpServletRequest.class,defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL, adapters = {PageExporter.class}, resourceType = PageExporterImpl.RESOURCE_TYPE)
@exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, selector = "mobile", extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class PageExporterImpl extends PageImpl implements PageExporter {
protected static final String RESOURCE_TYPE = "contentservices/components/structure/page";
@Override
public String getExportedType() {
return null;
}
@Override
public Map<String, ? extends ComponentExporter> getExportedItems() {
Map<String, ? extends ComponentExporter> componentMap = super.getExportedItems();
Set<String> childKeyList = super.getExportedItems().keySet();
for (String childKey : childKeyList) {
ResponsiveGrid res= (ResponsiveGrid) componentMap.get(childKey);
Map<String, ? extends ComponentExporter> componentMap1 = res.getExportedItems();
return componentMap1;
}
return componentMap;
}
@Override
public String getLanguage() {
return currentPage == null ? Locale.getDefault().toLanguageTag()
: currentPage.getLanguage(false).toLanguageTag();
}
}
Actually we need to manipulate the getExportedItems method, as its provide the full JSON structure like below but we only want items and itemsOrder from inside the root.
getExportedItems method of Core Page (PageImpl.java), just returns the Map of child nodes with its respective model class.
In the screenshot above, root is the child node name with resourceType set as wcm/foundation/components/responsivegrid (:type).
So in the screenshot you have shared is the getters(columnCount till :type) exposed by ResponsiveGrid Model (com.day.cq.wcm.foundation.model.responsivegrid.ResponsiveGrid)
Given this, the requirement you stated now is like altering the OOTB ResponsiveGrid model or since you are doing POC, perhaps you can give a try of custom responsivegrid component that inherits from OOTB grid and hence provide custom implementation.