Custom Sling Model Injector using Annotations
Published
@TagProperty
, to designate fields in the Sling model that require specific data injection. There are three parts to consider in order to build a custom injector:- Custom Annotation: Defines the properties for injection.
- Annotation Processor: Manages annotation-based injection.
- Custom Injector: Handles the actual injection of values into the model.
Custom Annotation
@TagProperty
annotation, similar to standard annotations like @ValueMapValue
or @RequestAttribute
, to be used within the Sling Model.models / injectors / annotations / TagProperty.java
public TagProperty {
String name() default StringUtils.EMPTY;
String SOURCE = "tag-property";
InjectionStrategy injectionStrategy() default InjectionStrategy.DEFAULT;
}
Annotation Processor
StaticInjectAnnotationProcessorFactory
interface, it connects the custom annotation with the injector, mapping annotation attributes to the Sling framework.models / injectors / annotations / impl / TagPropertyAnnotationProcessorFactory.java
public class TagPropertyAnnotationProcessorFactory
implements StaticInjectAnnotationProcessorFactory {
public InjectAnnotationProcessor2 createAnnotationProcessor(AnnotatedElement element) {
return Optional.ofNullable(
element.getAnnotation(TagProperty.class)
).map(TagPropertyAnnotationProcessorFactory.PagePropertyAnnotationProcessor::new)
.orElse(null);
}
private static class PagePropertyAnnotationProcessor
extends AbstractInjectAnnotationProcessor2 {
private final TagProperty annotation;
public PagePropertyAnnotationProcessor(TagProperty annotation) {
this.annotation = annotation;
}
public String getName() {
return StringUtils.isBlank(annotation.name()) ? null : annotation.name();
}
public InjectionStrategy getInjectionStrategy() {
return annotation.injectionStrategy();
}
}
}
Custom Injector
models / injectors / TagPropertyInjector.java
public class TagPropertyInjector implements Injector {
private static final Logger logger = LoggerFactory.getLogger(TagPropertyInjector.class);
public String getName() {
return TagProperty.SOURCE;
}
public Object getValue(
Object adaptable,
String name,
Type declaredType,
AnnotatedElement element,
DisposalCallbackRegistry callbackRegistry
) {
if (element.isAnnotationPresent(TagProperty.class)) {
TagProperty annotation = element.getAnnotation(TagProperty.class);
ResourceResolver resourceResolver = getResourceResolver(adaptable);
if (resourceResolver == null) {
logger.error("ResourceResolver is null, cannot inject tag property");
return null;
}
TagManager tagManager = resourceResolver.adaptTo(TagManager.class);
if (tagManager == null) {
logger.error("TagManager is null, cannot inject tag property");
return null;
}
String key = StringUtils.defaultIfEmpty(annotation.name(), name);
final String[] tagKeys = getResource(adaptable).getValueMap().get(key, String[].class);
if (tagKeys == null || tagKeys.length == 0){
return null;
}
final Stream<Tag> tagStream = Arrays.stream(tagKeys).map(tagManager::resolve);
return tagStream.filter(Objects::nonNull).findFirst().orElse(null);
}
return null;
}
private static ResourceResolver getResourceResolver(Object adaptable) {
if (adaptable instanceof SlingHttpServletRequest) {
return ((SlingHttpServletRequest) adaptable).getResourceResolver();
}
if (adaptable instanceof Resource) {
return ((Resource) adaptable).getResourceResolver();
}
return null;
}
private static Resource getResource(Object adaptable) {
if (adaptable instanceof SlingHttpServletRequest) {
return ((SlingHttpServletRequest) adaptable).getResource();
}
if (adaptable instanceof Resource) {
return (Resource) adaptable;
}
return null;
}
}
components / models / ArticleModel.java
public class ArticleModel {
private Tag topics;
public Tag getTopics() {
return topics;
}
}