The Resource
and ResourceResolver
interfaces are defined with a method adaptTo
, which adapts the object to other classes. Using this mechanism the JCR session of the resource resolver calling the adaptTo
method with the javax.jcr.Session
class object. Likewise the JCR node on which a resource is based can be retrieved by calling the Resource.adaptTo
method with the javax.jcr.Node
class object.
To use resources as scripts, the Resource.adaptTo
method must support being called with the org.apache.sling.api.script.SlingScript
class object. But of course, we do not want to integrate the script manager with the resource resolver. 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. The single method defined by this interface is
/**
* Adapts the adaptable to another type.
* <p>
* Please not that it is explicitly left as an implementation detail whether
* each call to this method with the same <code>type</code> yields the same
* object or a new object on each call.
* <p>
* Implementations of this method should document their adapted types as
* well as their behaviour with respect to returning newly created or not
* instance on each call.
*
* @param <AdapterType> The generic type to which this resource is adapted
* to
* @param type The Class object of the target type, such as
* <code>javax.jcr.Node.class</code> or
* <code>java.io.File.class</code>
* @return The adapter target or <code>null</code> if the resource cannot
* adapt to the requested type
*/
<AdapterType> AdapterType adaptTo(Class<AdapterType> type);
This method is called to get a view of the same object in terms of another class. Examples of implementations of this method are the Sling ResourceResolver
implementation providing adapting to a JCR session and the Sling JCR based Resource
implementation providing adapting to a JCR node.
The Web Console Plugin at /system/console/adapters
and at /system/console/status-adapters
can be used to list all existing adaptables in the system with their according adapter classes.
The web console plugin evaluates metadata being provided by any AdapterFactory
services as well as metadata being provided through the file SLING-INF/adapters.json
Each adaptable should derive from SlingAdaptable
to automatically profit from all according AdapterFactories
registered in the system. In case the adaptTo(...)
method is being overwritten an according SLING-INF/adapters.json
should be included in the providing bundle listing all adaptation possibilities. While this file is not strictly necessary for the actual adaptation to work, it provides useful information to the Web Console plugin. Otherwise developers will not know which adaptations are supported. The format of this JSON file looks like this (SLING-2295):
{
<fully qualified class name of adaptable> : {
<condition> : <fully qualified class name of adapter, may be a JSON array>
}
}
For example
{
"org.apache.sling.api.resource.Resource" : {
"If the adaptable is a AuthorizableResource." : [
"java.util.Map",
"org.apache.sling.api.resource.ValueMap",
"org.apache.jackrabbit.api.security.user.Authorizable"
],
"If the resource is an AuthorizableResource and represents a JCR User" : "org.apache.jackrabbit.api.security.user.User",
"If the resource is an AuthorizableResource and represents a JCR Group" : "org.apache.jackrabbit.api.security.user.Group"
}
}
The Adapter Annotations provide OSGi DS 1.4 component property types that ease implementing Adaptable
services.
For adaptable implementations not provided by a dedicated OSGi service implementing Adaptable
you should consider creating the JSON file described in the previous section with the help of the annotations from the module Adapter Annotations (JSON) which are evaluated by goal generate-adapter-metadata
from the Maven Sling Plugin (SLING-2313).
Sometimes an Adaptable
implementation cannot foresee future uses and requirements. To cope with such extensibility requirements two interfaces and an abstract base class are defined:
AdapterManager
AdapterFactory
SlingAdaptable
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. The adaptable object is
* guaranteed to be an instance of one of the classes listed in the
* {@link #ADAPTABLE_CLASSES} services registration property. The type
* parameter is one of the classes listed in the {@link #ADAPTER_CLASSES}
* service registration properties.
* <p>
* This method may return <code>null</code> if the adaptable object cannot
* be adapted to the adapter (target) type for any reason. In this case, the
* implementation should log a message to the log facility noting the cause
* for not being able to adapt.
* <p>
* Note that the <code>adaptable</code> object is not required to implement
* the <code>Adaptable</code> interface, though most of the time this method
* is called by means of calling the {@link Adaptable#adaptTo(Class)}
* method.
*
* @param <AdapterType> The generic type of the adapter (target) type.
* @param adaptable The object to adapt to the adapter type.
* @param type The type to which the object is to be adapted.
* @return The adapted object or <code>null</code> if this factory instance
* cannot adapt the object.
*/
<AdapterType> AdapterType getAdapter(Object adaptable,
Class<AdapterType> type);
Implementations of this interface are registered as OSGi services providing two lists: The list of classes which may be adapted (property named adaptables
) and the list of classes to which the adapted class may be adapted (property named adapters
). A good example of an Class implementing AdapterFactory
is the SlingScriptAdapterFactory
. In addition a property named adapter.condition
can be provided which is supposed to contain a string value explaining under which circumstances the adaption will work (if there are any restrictions). This is evaluated by the Web Console Plugin.
AdapterFactory
services are gathered by a AdapterManager
implementation for use by consumers. Consumers should not care for AdapterFactory
services.
If multiple implementations for the same combination of adapter and adaptable are registered, the implementation with the lowest service ranking wins (or with the highest service id, if the ranking is the same).
The AdapterManager
is defines the service interface for the generalized and extensible use of AdapterFactory
services. Thus the adapter manager may be retrieved from the service registry to try to adapt whatever object that needs to be adapted - provided appropriate adapters exist.
The AdapterManager
interface is defined as follows:
/**
* Returns an adapter object of the requested <code>AdapterType</code> for
* the given <code>adaptable</code> object.
* <p>
* The <code>adaptable</code> object may be any non-<code>null</code> object
* and is not required to implement the <code>Adaptable</code> interface.
*
* @param <AdapterType> The generic type of the adapter (target) type.
* @param adaptable The object to adapt to the adapter type.
* @param type The type to which the object is to be adapted.
* @return The adapted object or <code>null</code> if no factory exists to
* adapt the <code>adaptable</code> to the <code>AdapterType</code>
* or if the <code>adaptable</code> cannot be adapted for any other
* reason.
*/
<AdapterType> AdapterType getAdapter(Object adaptable,
Class<AdapterType> type);
Any object can theoretically be adapted to any class even if it does not implement the Adaptable
interface, if an AdapterFactory
service delivers a getAdapter()
method which adapts an object to another one. To check if there's any existing AdapterFactory
which can adapt a given object to another one the AdapterManager
service with its getAdapter()
method does the job. So the Adaptable
interface merely is an indicator that the object provides built-in support for being adapted.
The SlingAdaptable
class is an implementation of the Adaptable
interface which provides built-in support to call the AdapterManager
to provide an adapter from the Adaptable
object to the requested class.
An example of extending the SlingAdaptable
class will be the Sling JCR based Resource
implementation. This way, such a resource may be adapted to a SlingScript
by means of an appropriately programmed AdapterFactory
(see below).