You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

Many Sling projects want to be able to create model objects - POJOs which are automatically mapped from Sling objects, typically resources, but also request objects. Sometimes these POJOs need OSGi services as well.

YAMF is an attempt to consolidate the various approaches I have seen to this problem.

Design Goals

  • Entirely annotation driven. "Pure" POJOs.
  • Use standard annotations where possible.
  • Pluggable
  • OOTB, support resource properties (via ValueMap), SlingBindings, OSGi services, request attributes
  • Adapt multiple objects - minimal required Resource and SlingHttpServletRequest
  • Client doesn't know/care that YAMF is involved
  • Support both classes and interfaces.
  • Work with existing Sling infrastructure (i.e. not require changes to other bundles).

Basic Usage

In the simplest case, the class is annotated with @Model and the adaptable class. Fields which need to be injected are annotated with @Inject:

@Model(adaptables=Resource.class)
public class MyModel {
 
    @Inject
    private String propertyName;
}

In this case, a property named 'propertyName' will be looked up from the Resource (after first adapting it to a ValueMap) and it is injected.
 
For an interface, it is similar:

@Model(adaptables=Resource.class)
public interface MyModel {
 
    @Inject
    String getPropertyName();
}


In order for these classes to be picked up, there is a header which must be added to the bundle's manifest:

<Sling-YAMF-Packages>
  org.apache.sling.yamf.it.models
</Sling-YAMF-Packages> 


Client Code

Client code doesn't need to be aware that YAMF is being used. It just uses the Sling Adapter framework:



Other Options

If the field or method name doesn't exactly match the property name, @Named can be used:

@Model(adaptables=Resource.class)
public class MyModel {
 
    @Inject @Named("secondPropertyName")
    private String otherName;
} 
 


@Injected fields/methods are assumed to be required. To mark them as optional, use @Optional:

@Model(adaptables=Resource.class)
public class MyModel {
 
    @Inject @Optional
    private String otherName;
}
 


OSGi services can be injected:

@Model(adaptables=Resource.class)
public class MyModel {
 
    @Inject
    private ResourceResolverFactory resourceResolverFactory;
} 


 
In this case, the name is not used -- only the class name. Lists and arrays are supported:

@Model(adaptables=Resource.class)
public class MyModel {
 
    @Inject
    private List<Servlet> servlets;
}


 
OSGi injection can be filtered:

@Model(adaptables=SlingHttpServletRequest.class)
public class MyModel {
 
    @Inject
    private PrintWriter out;
 
    @Inject
    @Named("log")
    private Logger logger;
 
    @Inject
    @Filter("paths=/bin/something")
    private List<Servlet> servlets;
}


 
The @PostConstruct annotation can be used to add methods which are invoked upon completion of all injections:

@Model(adaptables=SlingHttpServletRequest.class)
public class MyModel {
 
    @Inject
    private PrintWriter out;
 
    @Inject
    @Named("log")
    private Logger logger;
 
    @PostConstruct
    protected void sayHello() {
         logger.info("hello");
    }
}
 


@PostConstruct methods in a super class will be invoked first.
 
Using projection, you can inject based on a "child" object of the adaptable. 

@Model(adaptables=SlingHttpServletRequest.class)
public interface MyModel {
 
    @Inject @Projection("resource")
    String getPropertyName();
} 


  • No labels