in the last days we played around with Sling Models as underpinning of our 
views in a Sling+Sightly based application. it works fantastic. but during our 
experiments we detected that we were not using Sling Models for accessing 
resource content, i.e. we are not adapting resources to models to access its 
data. this is not required when using JSP or Sightly because you can access the 
properties directly. but we were using Sling Models as Dependency Injection 
frameworks for our java controller classes which are used behind the views, 
which need access to scoped objects of the current request like resource 
resolver and others. and if those controller classes need other business 
classes which depend on scope objects, we use Sling Models injection for them 
as well.

a very simple example for illustration:

this is a controller class behind the view:

@Model(adaptables = SlingHttpServletRequest.class)
public class MyController {

  @Inject
  private MyBusinessClass businessClass;

  private String result;

  @PostConstruct
  protected void activate() {
    result = this.businessClass.calculateTheResult();
  }

  public String getResult() {
    return this.result;
  }

}

and this is the business classes used by the controller

@Model(adaptables = SlingHttpServletRequest.class)
public class MyBusinessClass {

  @Inject
  private ResourceResolver resourceResolver;

  public String calculateTheResult() {
    // access resources using resource resolver
    return "myResult";
  }

}

effectively we can use Sling Models to navigate through a hierarchy of loose 
coupled POJOs all using DI for getting the dependencies they need - much like 
in Spring. we have already dependency injection in Felix SCR/OSGi? that's 
right, but only for OSGi services, and not for "scoped" objects depending on a 
current request or resource resolver instance.

using the adapter concept to adapt the controller and business classes from the 
scope object they depend on (e.g. from SlingHttpServletRequest for request 
scope, from ResourceResolver if they need access to resources in context of a 
JCR user session etc.) they get all scope context objects they need, and those 
that can be adapted from them. and of course it's still possible to inject OSGi 
services as well.

out of the box the sample displayed above will not work with Sling Models - a 
small custom injector is required:

@Component
@Service
@Property(name = Constants.SERVICE_RANKING, intValue = 50)
public class SelfAdaptInjector implements Injector {

  public String getName() {
    return "selfadapt";
  }

  public Object getValue(Object pAdaptable, String pName, Type pType, 
AnnotatedElement pElement, DisposalCallbackRegistry pCallbackRegistry) {
    if (!(pType instanceof Class)) {
      return null;
    }
    if (!(pAdaptable instanceof Adaptable)) {
      return null;
    }
    return ((Adaptable)pAdaptable).adaptTo((Class<?>)pType);
  }

}

which effectively supports navigation through a graph of objects which share 
the same scope adaptable. using another custom injector which allows indirect 
adaption from SlingHttpServletRequest->ResourceResolver->CustomClass it is 
possible to have classes that can be used both in request scope, and in 
ResourceResolver-only scope as well (the latter may be a resource resolver 
instance which is opened in context of an OSGi service or event handler). this 
is already possible via "via", but i want to avoid boilerplate code as much as 
possible for much-used context objects.

additionally all relevant "context" objects that can be derived from the 
adaptable should be supported for injection. Justin already added in SLING-3700 
as custom injector for resource resolver, but it should support others as well, 
e.g. injecting the request, the response etc. and in my opinion this should be 
done primary using detection by class type, not by property name (this can be 
controlled already by injector ordering and custom injectors).

if Sling is running in other context like AEM an AEM specific injector can 
ensure that AEM-typical context objects are available as well (again Justin has 
an example for this in AEM commons, although based on property names and not on 
class types).

to be even more Spring-like and support developers with Spring background it 
would be nice to support other Spring-typical features as well, e.g. 
constructor dependency injection (currently only supported for the adaptable 
itself) and JSR-250 @PreDestroy annotation. all this should not be an big 
issue, because most infrastructure is already there in the current codebase.

is it worth to make this a core feature for Sling Models, and propagate it in 
documentation etc.?

WDYT?

stefan

Reply via email to