Expand my Community achievements bar.

SOLVED

AEM Sling custom injector @Source issue.

Avatar

Level 5

I have written my own custom annotation 'Jeroen':

import com.asadventure.platform.component.text.JeroenInjector;

import org.apache.sling.models.annotations.Source;

import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target({ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

@InjectAnnotation

@Source(JeroenInjector.NAME)

public @interface Jeroen {

String property();

}

And a custom injector:

import com.asadventure.core.constant.PropertyConstants;

import com.day.cq.commons.inherit.HierarchyNodeInheritanceValueMap;

import com.day.cq.commons.inherit.InheritanceValueMap;

import com.day.cq.wcm.api.Page;

import com.day.cq.wcm.api.PageManager;

import org.apache.commons.lang3.StringUtils;

import org.apache.sling.api.SlingHttpServletRequest;

import org.apache.sling.api.resource.Resource;

import org.apache.sling.api.resource.ResourceResolver;

import org.apache.sling.models.spi.DisposalCallbackRegistry;

import org.apache.sling.models.spi.Injector;

import org.osgi.framework.Constants;

import org.osgi.service.component.annotations.Component;

import java.lang.reflect.AnnotatedElement;

import java.lang.reflect.Type;

@Component(service = {Injector.class}, property = {

Constants.SERVICE_RANKING + ":Integer=" + 4300

})

public class JeroenInjector implements Injector {

public static final String NAME = "jeroen-injector";

@Override

public String getName() {

return NAME;

}

@Override

public Object getValue(final Object adaptable, final String name, final Type type, final AnnotatedElement element,

   final DisposalCallbackRegistry callbackRegistry) {

// Only class types are supported

if (!(type instanceof Class<?>)) {

return null;

}

Class<?> requestedClass = (Class<?>) type;

Resource resource = getResource(adaptable);

Page page = getResourcePage(adaptable);

if (requestedClass.equals(String.class)) {

return getValueFromInheritanceMap("as:hrefLang", resource, page);

}

return null;

}

//... more logic here

}

It seems that the getValue in the jeroenInjector class also gets called for properties that are NOT annotated with my custom '@Jeroen' annotation. I tought the @Source on my annotation would prevent this from happening:

Screen Shot 2018-01-23 at 15.34.24.png

@Inject

@Optional

@Named("configPageImage/fileReference")

String pageImageReference;

I only expect fields annotated with @Jeroen to call my injectors getValue method as here:

@Jeroen(property = "as:hrefLang")

private String jeroenTest;

What am I doing wrong? Thanks in advance

1 Accepted Solution

Avatar

Correct answer by
Level 2

The way sling injectors work is the following:

when an element is annotated with @Inject, ALL injector services (including your custom one) are called according to their ranking untill a non-null value is returned if no injector returns a non-null value, null is returned.

When using a custom injector, only the injector service registered to that injector is called.

Your custom injector seems very specific, so you'll need an exit case in your injector, something like:

"if property option is null, return null"
or "If element where injector is used is not annotated with @Jeroen return null"

or some other specific condition so that your injector always returns null except for the specific condition you use it for.

View solution in original post

6 Replies

Avatar

Level 10

This looks like a really good topic to base a Community article on. We do not have an example that I can refer you to. However - here is a community artilce that may help:

Sling Models: How to write Sling Model Injector - Bots & AEM corner

Also - i am asking internal ppl.

Avatar

Level 10

Our team also suggested you look here to see a code example that works:

ACS Commons has a feature that does this: https://adobe-consulting-services.github.io/acs-aem-commons/features/sling-model-injectors/aem-objec...

Avatar

Employee

This is an not the way Sling Models works by default. If there is no @Source annotation (or no custom injector which provides the source value), all injectors are checked. The @Source annotation is just used to refine the scope of annotations checked. You can certainly achieve what you are describing in your custom injector by returning null if the annotation isn't present.

Avatar

Correct answer by
Level 2

The way sling injectors work is the following:

when an element is annotated with @Inject, ALL injector services (including your custom one) are called according to their ranking untill a non-null value is returned if no injector returns a non-null value, null is returned.

When using a custom injector, only the injector service registered to that injector is called.

Your custom injector seems very specific, so you'll need an exit case in your injector, something like:

"if property option is null, return null"
or "If element where injector is used is not annotated with @Jeroen return null"

or some other specific condition so that your injector always returns null except for the specific condition you use it for.

Avatar

Level 5

So the @Inject annotation has no @Source annotation pointing to a specific injector so they all pass my customer injector? By "or no custom injector which provides the source value" you mean that the getName method of the injector returns a value matching the value in the @Source annotation?

Avatar

Level 5

I could use something like here? sling-models-demo/ResourcePathInjector.java at master · justinedelson/sling-models-demo · GitHub

ResourcePath path = element.getAnnotation(ResourcePath.class);

if (path == null) {

  return null;

}