Search
  • Juan Ayala

The Sling Model @Via Annotation

I love writing code. What I love more is writing less. That is why I like Sling models. I have written about leveraging its injectors. And how to create custom ones. Now I will cover the @Via annotation, the OOTB providers, and how to create custom ones.


What @Via Does


Sling relies heavily on the adapter pattern. This is the pattern used by the Sling model framework. The "adaptable" is the thing that will adapt to an instance of that model. This more often than not is a Resource or SlingHttpServletRequest. But it is not limited to these.


The Sling model injection will look into the adaptable and try to resolve the value it needs to inject. For example, see the @ValueMapValue annotation and its injector.


What @Via does is swap out the original adaptable for something else. The ModelAdapterFactory will use the new adaptable from that point on.


So what are some potential use-cases?

​⚠️ In the following examples I am using Groovy to implement the models and OSGi components. I am also using Spock to write unit tests. If you are using Java, these examples are easily translated into Java. If you are interested in Groovy & Spock, read my other articles.

Extending Core Components with Sling Delegation


The correct way to customize the sling model of a core component is with the delegation pattern. Interview answer 😉.


Let's take for example the Button component. There are 2 versions of this. V2 ButtonImpl extends V1. Those are internal implementation classes. I can not extend them in my own project because I do not have visibility into them. I have to use @Via and specify the ResourceSuperTypeViaProvider. First, in a TDD fashion, let's write some unit tests.

The whole purpose of Spock and GWT is to convey the intention of the code. So this test should be pretty self-explanatory. Now we can write the model

Using the @Via(type = ResourceSuperType) annotation will invoke the ResourceSuperTypeViaProvider. That will get the resource supertype and create a resource wrapper. This will be the new adaptable. Because the field type is Button, the model factory will adapt the new adaptable to a Button.


I am leveraging Groovy's @Delegate. That is a mere convenience. If you are using Java, you can use Lombok's @Delegate. Or you can do it the old-fashioned way and write a lot of boilerplate 🤨.


Getting The Adaptable Via A Bean Property


We are going to extend our model further. The following unit test should convey the new functionality. That is, when there is no topic property set, the priority will be the button's topic tag.

Here is the updated model

To resolve the tags, we will need a TagManager. This is an Adobe OSGi component that is obtained by adapting the ResourceResolver. Something like this

To simplify things we are going to use @Via(value = 'resourceResolver'). By default, it will use the BeanPropertyViaProvider. The original adaptable is a SlingHttpServletRequest. The provider will get the new adaptable from its "resourceResolver" property. The model factory will adapt the new adaptable to a TagManager because that is the target field type.


Writing a Custom @Via Provider


The last thing is to fetch the topic from the page tag when it is not found in the component tags or property. This is usually done with something like this

To simplify things and make reusable code, let's write a custom via type provider to get a page property.


There are 4 via providers in the framework and we already covered the first 2

As you can see, they are not hard to write. As the documentation states, you need two things.


A marker class that will be used by the @Via annotation

And an OSGi service

Make usre to update the unit test setup function by registering the new OSGi service with the context

Here is the updated model

Using the @Via(type = PageProperty) annotation will invoke the custom provider. It will return the page property as a resource. This will be the new adaptable. Because the field type is String[], the model factory will adapt the new adaptable to String[].


Conclusion


By and large, the primary reason to use @Via would be with the ResourceSuperType provider. This is the correct way to extend core component models.


Having a better understanding of how it works will help you cut down on the glue code. As you can see from the example, the framework is the glue. This allows the model to focus on picking one prefix for the button's text. Take a look at the other 2 providers I didn't cover. ChildResourceViaProvider and ForcedResourceTypeViaProvider .


Groovy & Spock make it more fun. You may have spotted a bug not covered by the unit tests. What happens when the page or component has tags, but none are topic tags? Write a unit test. See the failure. And then take a look a Groovy's safe navigation operator.

268 views0 comments

Recent Posts

See All