The Apache Sling Model enables injector specific annotations which aggregate the standard annotations for each of the available injector, which are: Script Bindings, Value Map, Resource Path, Child Resources, Request Attributes, OSGI Services, Self, and the Sling Object.
Sure we can invoke injectors by the @inject, followed by the @source annotation (with an injector name) as so, @Inject @Source(“script-bindings”), but invoking such injectors introduces many more lines of code which is tedious and repetitive. Using the @inject annotation freely may cause injector collisions.
The injector specific annotations enable us, developers, to write less code, enables stability with injectors to demise injector collisions, and enables better IDE support.
This article will provide examples (used in practice) which will include both ways to invoke injectors in Sling Models, using the @Inject & @Source annotations, and also the Apache Sling Model injector specific annotations approach.
Available Injectors
- @ScriptVariable
- @ValueMapValue
- @ResourcePath
- @ChildResource
- @RequestAttribute
- @OSGiService
- @Self
- @SlingObject
1. Script Bindings (name=”script-bindings”) Injector
Service Ranking: 1000
Annotation: @ScriptVariable
Description: Injects objects via script variable defined from Sling Bindings; Lookup objects in the script bindings object by name.
Without the injector specific annotations:
- 1. @Inject @Source(“script-bindings”) @Named(“component”)
- 2. @Inject @Source(“script-bindings”)
With the injector specific annotations:
- 3. @ScriptVariable(name = “component”)
- 4. @ScriptVariable
Note: If the name is not set (using the @Named annotation or name property), then the name is derived from the method/property/variable/field name.
A full list of the scripting variables can be found here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | @Model(adaptables = {Resource.class, SlingHttpServletRequest.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class ExampleComponent { // @Inject @Source("script-bindings") @Named("component") // @Inject @Source("script-bindings") // @ScriptVariable(name = "component") @ScriptVariable private Component component; // @Inject @Source("script-bindings") @Named("componentContext") // @Inject @Source("script-bindings") @ScriptVariable private ComponentContext componentContext; // @Inject @Source("script-bindings") @Named("currentDesign") // @Inject @Source("script-bindings") @ScriptVariable private Design currentDesign; // @Inject @Source("script-bindings") @Named("currentNode") // @Inject @Source("script-bindings") @ScriptVariable private Node currentNode; // @Inject @Source("script-bindings") @Named("currentPage") // @Inject @Source("script-bindings") @ScriptVariable private Page currentPage; // @Inject @Source("script-bindings") @Named("currentSession") // @Inject @Source("script-bindings") @ScriptVariable private HttpSession currentSession; // @Inject @Source("script-bindings") @Named("currentStyle") // @Inject @Source("script-bindings") @ScriptVariable private Style currentStyle; // @Inject @Source("script-bindings") @Named("designer") // @Inject @Source("script-bindings") @ScriptVariable private Designer designer; // @Inject @Source("script-bindings") @Named("editContext") // @Inject @Source("script-bindings") @ScriptVariable private EditContext editContext; // @Inject @Source("script-bindings") @Named("log") // @Inject @Source("script-bindings") @ScriptVariable private Logger log; // @Inject @Source("script-bindings") @Named("out") // @Inject @Source("script-bindings") @ScriptVariable private PrintWriter out; // @Inject @Source("script-bindings") @Named("pageManager") // @Inject @Source("script-bindings") @ScriptVariable private PageManager pageManager; // @Inject @Source("script-bindings") @Named("pageProperties") // @Inject @Source("script-bindings") @ScriptVariable private ValueMap pageProperties; // @Inject @Source("script-bindings") @Named("reader") // @Inject @Source("script-bindings") @ScriptVariable private BufferedReader reader; // @Inject @Source("script-bindings") @Named("request") // @Inject @Source("script-bindings") @ScriptVariable private SlingHttpServletRequest request; // @Inject @Source("script-bindings") @Named("resolver") // @Inject @Source("script-bindings") @ScriptVariable private ResourceResolver resolver; // @Inject @Source("script-bindings") @Named("resource") // @Inject @Source("script-bindings") @ScriptVariable private Resource resource; // @Inject @Source("script-bindings") @Named("resourceDesign") // @Inject @Source("script-bindings") @ScriptVariable private Design resourceDesign; // @Inject @Source("script-bindings") @Named("resourcePage") // @Inject @Source("script-bindings") @ScriptVariable private Page resourcePage; // @Inject @Source("script-bindings") @Named("response") // @Inject @Source("script-bindings") @ScriptVariable private SlingHttpServletResponse response; // @Inject @Source("script-bindings") @Named("sling") // @Inject @Source("script-bindings") @ScriptVariable private SlingScriptHelper sling; // @Inject @Source("script-bindings") @Named("slyWcmHelper") // @Inject @Source("script-bindings") @ScriptVariable private WCMScriptHelper slyWcmHelper; // @Inject @Source("script-bindings") @Named("wcmmode") // @Inject @Source("script-bindings") @ScriptVariable private SightlyWCMMode wcmmode; // @Inject @Source("script-bindings") @Named("xssAPI") // @Inject @Source("script-bindings") @ScriptVariable private XSSAPI xssAPI; } |
2. Value Map (name=”valuemap”) Injector
Service Ranking: 2000
Annotation: @ValueMapValue
Description: Gets a property from a ValueMap by name; If @Via is not set, it will automatically take resource if the adaptable is the SlingHttpServletRequest. If name is not set the name is derived from the method/field name.
1 2 3 4 5 6 7 8 9 10 11 12 | @Model(adaptables = SlingHttpServletRequest.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class ExampleComponent { // @Inject @Source("valuemap") @Named("jcr:title") @ValueMapValue(name = "jcr:title") private String titleText; // @Inject @Source("valuemap") @ValueMapValue private String titleDescription; } |
3. Resource Path (name=”resource-path”) Injector
Service Ranking: 2500
Annotation: @ResourcePath
Description: Injects one or multiple resources. The resource paths are either given by @Path annotations, the element path or paths of the annotation @ResourcePath or by paths given through a resource property being referenced by either @Named or element name of the annotation @ResourcePath.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class ExampleComponent { // @Inject @Source("resource-path") @Path("/content/sourcedcode/en/home") @ResourcePath(path = "/content/sourcedcode/en/home") Resource sourcedCodePageResource; // @Inject @Source("resource-path") @Path("/content/we-retail/language-masters/en") @ResourcePath(name = "/content/we-retail/language-masters/en") Resource weRetailPageResource; // @Inject @Source("resource-path") @Path(paths = {"/content/sourcedcode/en/home","/content/we-retail/language-masters/en"}) @ResourcePath(paths = {"/content/sourcedcode/en/home","/content/we-retail/language-masters/en"}) Resource[] resources; } |
4. Child Resources (name=”child-resources”) Injector
Service Ranking: 3000
Annotation: @ChildResource
Description: Gets a child resource by name.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class ExampleComponent { // @Inject @Source("child-resources") @Named("links") // @ChildResource(name="links") @ChildResource private Resource links; // @Inject @Source("child-resources") @Named("links") // @ChildResource(name="links") @ChildResource private List<Resource> links; // @Inject @Source("child-resources") @Named("social") // @ChildResource(name="social") @ChildResource private Resource social; } |
5. Request Attributes (name=”request-attributes”) Injector
Service Ranking: 4000
Annotation: @RequestAttribute
Description: Injects a request attribute by name. If name is not set the name is derived from the method/field name.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Model(adaptables = SlingHttpServletRequest.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class ExampleComponent { // @Inject @Source("request-attributes") @Named("social") @RequestAttribute(name = "social") private String socialParam; // @Inject @Source("request-attributes") @Named("link") // @RequestAttribute(name = "link") @RequestAttribute private String link; public String getSocialParam() { return socialParam; } public String getLink() { return link; } } |
The example below calls the Sling Model using input parameter:
1 2 3 4 | <sly data-sly-use.example="${'com.sourcedcode.core.models.ExampleComponent' @ social=currentPage.title,link=currentPage.path}"> ${example.socialParam} ${example.link} </sly> |
6. OSGi Services (name=”osgi-services”) Injector
Service Ranking: 5000
Annotation: @OSGiService
Description: Injects an OSGi service by type; Lookup services based on class name. Since Sling Models Impl 1.2.8 (SLING-5664) the service with the highest service ranking is returned. In case multiple services are returned, they are ordered descending by their service ranking (i.e. the one with the highest ranking first).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class ExampleComponent { // @Inject @Source("osgi-services") @OSGiService private SlingSettingsService slingSettingsService; // @Inject @Source("osgi-services") @OSGiService private MyCustomOSGIService myCustomOSGIService; // @Inject @Source("osgi-services") @OSGiService private MyCustomOSGISConfigurationervice myCustomOSGISConfigurationervice; } |
7. Self (name=”self”) Injector
Service Ranking: Integer.MAX_VALUE
Annotation: @Self
Description: Injects the adaptable object itself (if the class of the field matches or is a supertype). If the @Self annotation is present it is tried to adapt the adaptable to the field type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class ExampleComponent { // @Inject @Source("self") @Self private Node node; // @Inject @Source("self") @Self private MyCustomSlingModel myCustomSlingModel; } /////// /////// /////// Example below highlights that the @self annotation can minimize the lines of code that needs to be written. /////// /////// @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class ExampleComponent { @SlingObject private Resource currentResource; Node node; @PostConstruct public void init() { // adapts the current resource to a node class node = currentResource.adaptTo(Node.class); } } |
8. Sling Object (name=”sling-object”) Injector
Service Ranking: Integer.MAX_VALUE
Annotation: @SlingObject
Description: Injects commonly used sling objects if the field matches with the class: request, response, resource resolver, current resource, SlingScriptHelper. This works only if the adaptable can get the according information, i.e. all objects are available via SlingHttpServletRequest while ResourceResolver can only resolve the ResourceResolver object and nothing else. A discussion around this limitation can be found at SLING-4083. Also Resources can only be injected if the according injector specific annotation is used (@SlingObject).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class ExampleComponent { // @Inject @Source("sling-object") @SlingObject private SlingHttpServletRequest slingHttpServletRequest; // @Inject @Source("sling-object") @SlingObject private SlingHttpServletResponse slingHttpServletResponse; // @Inject @Source("sling-object") @SlingObject private Resource currentResource; // @Inject @Source("sling-object") @SlingObject private ResourceResolver resourceResolver; } |
If the name is not set (using the @Named annotation or name property), then the name is derived from the method/property/variable/field name. An example for setting the @Named annotation would time you as a developer encounter a clash between the method/property/variable/field name or when developer not wanting to use the scripting variable names as the variables in the POJO.
As you can see, using the Apache Sling Model’s injector specific annotations during implementation will help you stay organised, write less code, and speed up the development process.
Regarding [5. Request Attributes], if I’m not wrong this will only work if model is adaptable from SlingHttpServletRequest (adaptables = SlingHttpServletRequest.class)
@tom, thank you for pointing out this typo, I have edited the documentation, and I tested it, it should be working as expected now.
regarding 1) where adatables is Resource.class and you used
// @Inject @Source(“script-bindings”) @Named(“request”)
// @Inject @Source(“script-bindings”)
@ScriptVariable
private SlingHttpServletRequest request;
I dont think so request object we can get when adatables Is Resoruce.class
Thanks for letting me know, added @Model(adaptables = {Resource.class, SlingHttpServletRequest.class}, both classes to remove any confusion.