...
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:
Code Block |
|
|
@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:
Code Block |
|
|
@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:
Code Block |
|
|
<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:
Code Block |
|
|
@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:
Code Block |
|
|
@Model(adaptables=Resource.class)
public class MyModel {
@Inject @Optional
private String otherName;
}
|
OSGi services can be injected:
Code Block |
|
|
@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:
Code Block |
|
|
@Model(adaptables=Resource.class)
public class MyModel {
@Inject
private List<Servlet> servlets;
} |
OSGi injection can be filtered:
Code Block |
|
|
@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:
Code Block |
|
|
@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.
Code Block |
|
|
@Model(adaptables=SlingHttpServletRequest.class)
public interface MyModel {
@Inject @Projection("resource")
String getPropertyName();
} |
...