The Adobe AEM software provides several classes which can take an apache Sling Resource and adapt it to another class like so:
Page page = resource.adaptTo(Page.class);
To use this syntax with classes that you author and control this boils down to simply implementing the Adaptable interface.
However, if you want to enable a Resource to adaptTo your new custom class, is seems that you have to implement the AdapterFactory interface and register it in OSGI.
This is how the Adobe website describes it:
By an AdapterFactory, which can map arbitrary objects. The objects must still implement the Adaptable interface and must extend SlingAdaptable (which passes the adaptTo call to a central adapter manager). This allows hooks into the adaptTo mechanism for existing classes, such as Resource.
I have walked through the SlingScriptAdapterFactory code, but ultimately I am not connecting the dots here. Basically I want to do this:
MyClass myClass = Resource.adaptTo(MyClass.class);
Do I create a class that implements AdapterFactory and simply deploy it with the package expecting that Sling will just find it by type or is there more to it?
To cope with such extensibility requirements two interfaces and an abstract base class are defined: The AdapterFactory interface defines the service interface and API for factories supporting extensible adapters for SlingAdaptable objects. The interface has a single method: /** * Adapt the given object to the adaptable type.
This way, the script manager can provide an adapter factory to adapt Resource to SlingScript objects. The Adaptable interface defines the API to be implemented by a class providing adaptability to another class. The single method defined by this interface is This method is called to get a view of the same object in terms of another class.
To enable adapting objects to classes which are not foreseen by the original implementation, a factory mechanism is used. This way, the script manager can provide an adapter factory to adapt Resource to SlingScript objects. The Adaptable interface defines the API to be implemented by a class providing adaptability to another class.
Sling offers an Adapter pattern to conveniently translate objects that implement the Adaptable interface. This interface provides a generic adaptTo () method that will translate the object to the class type being passed as the argument.
Here is a little bit better documentation https://sling.apache.org/documentation/the-sling-engine/adapters.html
So you should implement the Adaptable interface, as you already described. Then create a properly annotated AdapterFactory:
@Component
@Service(value=org.apache.sling.api.adapter.AdapterFactory.class)
@Properties({
@Property(name = "adaptables", value = { "org.apache.sling.api.resource.Resource" }),
@Property(name = "adapters", value = { "org.sling.MyClass" })
})
public class MyAdapterFactory implements AdapterFactory{
public <AdapterType> AdapterType getAdapter(final Object adaptable, Class<AdapterType> type){
return new MyClassAdapter(adaptable);
}
}
Note that I've been working on a simpler way to create Sling adapters, by annotating methods with a new @Adapter annotation, as in
@Component
@Service
public class C implements AdapterMethodsProvider {
@Adapter
public CustomerRecord convert(Resource r) { ... }
@Adapter
public Person adaptToPerson(Resource r) { ... }
}
See https://issues.apache.org/jira/browse/SLING-2938 for details, but note that this is not even in the Sling trunk yet, so it will take some time before it's released and available in AEM/CQ.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With