sling models custom injector header image

Sling Models: How to write Sling Model Injector

Once I needed to write several debugging servlets for some functionality. Those servlets had to accept a big pile of parameters and I was tired of having tens of rows like   String paramValue = request.getParameter("paramName"). Luckily, I’ve already known Sling Models. So I’ve decided to find the way to inject these parameters into the model, so I could write just request.adaptTo(MyModel.class) . Unfortunately, I found nothing, so decided to write it myself. So in this article, I will show you how to write custom Sling Model injector on the example of Sling Model Request Parameter Injector.

Custom Sling Model Injector

To write it, we need to implement an OSGi service, which inherits from Injector interface.  It will force us to implement 2 methods:

  • getName – returns string, which will be used as logical name for the injector.
  • getValue – returns a value for an injection point.

The last one has several parameters and we will review some of them a bit later.

Sling Model Request Parameter Injector

Let’s imagine, that we have model RequestParamsInjected where we have several fields, which should be populated with request parameters.

For injecting values into the model fields, we need to annotate them. Actually, we can use existing annotations @Inject and @Source but it’s not flexible at all. We will write our own annotation!

We mark an annotation to be used with fields only, however you can have it for methods and parameters as well if you specify them in @Target. Then we declare our annotation as custom injection annotation. And finally, we specify which logical name to be used with for annotation.

Now we can mark fields with new annotations:

Without injector our annotation is useless. Let’s fix it:

Earlier we discussed briefly methods we override above. Now it’s time to go through the method getValue:

  • for now we will use first 3 arguments:
    • adaptable – object which Sling tries to adapt from. We expect it to be the request;
    • fieldName – name of the field. which has been annotated;
    • type – the declared type of the injection point;
  • first, we check if adaptable is request and type is Class;
  • then we call method getValue where we get parameter by field name and cast it to the integer if injection field is of type Integer;
  • finally, we return integer, string or null value.

For testing, let’s create simple component:

We are ready to test it:

sling model request parameter inject first example

As you can see, both parameters were injected into the fields successfully. But what if one of the parameters is not specified? Our model will not be adapted because both fields are required. Other annotations, like OSGiService, has nice attribute optional which can be used in such cases. Let’s add such feature for our annotation:

And now adapt model as well:

Why not test it now?

sling model inject optional not work

Oops, does not work. But why?

Custom Inject Annotation Processor

It appears, that to make custom annotation fully working, we need to implement one more interface – InjectAnnotationProcessorFactory (note that in latest versions this interface is marked as deprecated and StaticInjectAnnotationProcessorFactory should be used instead).

Let’s implement it.

There, we create annotation processor, which overrides isOptional and returns corresponding value from the annotation. After this changes, it works:

sling model request parameter optional

Custom Annotation Attribute

Let’s imagine that now we have new requirement – we need to get parameter from the request, but we do not know its name. However, we have a regular expression which we can use to find it.

First, we need to update our annotation – add regexp attribute to it:

Then we need to update injector:

There, before we can get value, we need to find parameter’s name if regular expression is provided. To do this, we iterate over all parameters and match them with regexp from our annotation.

And add a new field with specified regular expression:

Finally, let’s adapt our test component:

Test it? Test it!

sling models request parameter regular expression

Great, it works as expected.

Conclusion:

Even though we’ve implemented pretty basic Request Parameter injector, it can give you the insight of how to write more advanced version. Being able to write custom injectors gives nice tool for making your logic cleaner and more maintainable.

 

P.S.: will be happy to get feedback and questions from you and see you soon.

You can find the complete code in this GitHub repo.

4 thoughts on “Sling Models: How to write Sling Model Injector

  1. I am having an issue, all other controllers for example a class that has a field injected with @Inject also call the getValue of my new annotation. It seems that the @Source does not work. I am using aem 6.3.1.2. Any idea?

    1. JDRUWE, when you use @Inject, Sling does not know, which Injector should be used to inject the value, so it goes through all the Injectors and stops as soon as first non-null value is obtained.

Your thoughts are welcome