Our team has been working on adopting the sling model paradigm which has given 
me a chance to get involved with creating a number of injectors to meet our 
needs.

I wanted to share some of my observations. 
To clarify the existing flow, when we are looking at performing an injection it 
is really a three step process.

* Obtain the base Object from the supplied adaptable
* Retrieve a value from the base Object
* Manipulate the value to meet the type expectations of the annotated field

During any one of these steps, the result of the step could equal null and the 
process would then move on to the next injector.
While working on these injectors I came across a series of commonalities within 
their implementations. You could actually define a shorthand for the majority 
of the injectors, and extrapolating from that you could define an injector 
completely in annotations.

Example annotations derived from existing

For defining the base object
@adaptable(Class<?>)  The expected object to be used as the source of this 
injector  
@via(String)  use a JavaBean property of the adaptable as the source of the 
injection      
@viaArgument(Object*) if present, use this as the argument for the @via access
@adaptTo(Class<?>) if present attempts to adapt the current base Object

For retrieving the value
@method(String) name of the method to use.
@named(String) name of the argument for the above @method, defaults to the 
property name
@default(Object*) replaces the returned value if null.

Which, if you put the logic for these annotations in the framework, would allow 
you to do things like this mockup of a RequestAttribute injector:

public class RequestAttributeInjector {
    
    @adaptable(ServletRequest.class)  @method("getAttribute")
    Object getValue(Object adaptable, String name, Type declaredType, 
AnnotatedElement element, DisposalCallbackRegistry callbackRegistry){
        return  adaptable;
    }
}

where a SlingBindings injector would look like this

public class SlingBindingsInjector {

    @adaptable(ServletRequest.class) @via("attribute")
    @viaArgument(SlingBindings.class.name()) @method("get")
    Object getValue(Object adaptable, String name, Type declaredType, 
AnnotatedElement element, DisposalCallbackRegistry callbackRegistry){
        return  adaptable;
    }
    
}

You'll notice I didn't touch step 3. Because once you extract these concepts 
into separate steps, type conversion is the same for all objects and therefore 
something that should be supplied by the framework. This sort of separation 
would provide consistency of type conversion across different injectors.

My TL;DR comes down to
I believe that by adding additional meta information to the sling injector 
definition, either through annotations or properties, that you could streamline 
the functionality and make it easier to develop new injectors. An example being 
the declaration of what type of adaptable is expected and verifying that prior 
to calling the injector. 

The addition of the meta information could be added expanded to the point that 
most injectors could be defined as a series of properties.

Additionally, unifying the type conversion outside of the injector would be 
beneficial, as it provides a consistent type conversion process and enables 
improvements of that process to be shared by all injectors.

WDYT?

-Jason

Reply via email to