Resources

What is a Resource

The Resource is one of the central parts of Sling. Extending from JCR's Everything is Content, Sling assumes Everthing is a Resource. Thus Sling is maintaining a virtual tree of resources, which is a merger of the actual contents by so called resource providers (one of them usually the JCR Resource Provider). By doing this Sling fits very well in the paradigma of the REST architecture.

Resource Properties

Resources have a number of essential properties:

Property Description
Path Resources are part of a Resource Tree. As such each Resource has a path which is formed by concatenating the names of all Resources along the root to the Resource separated by a slash. Ok, really, this is much like a URL path or a file system path where the slash (/) is the separator character.
Name The name of the Resource is the last element (or segment) in the path.
Resource Type Each resource has a resource type which is used by the Servlet and Script resolver to find the appropriate Servlet or Script to handle the request for the Resource.
Resource Super Type The (optional explicit) super type of the Resource. See the section Resource Types below for more details.
Adapters Resources are always Adaptable and therefore can be adapted to a different view. See the section Resource Adapters below for more details.
Metadata Resources in general support ResourceMetadata providing access to values such as the length of a binary resource (which can be streamed) or the Resource's content type.

For a complete description of the Resource interface, please refer to the Resource JavaDoc.

The properties are either exposed via the ValueMap or ModifiableValueMap adaptable or by calling Resource.getValueMap().

To get the main binary property from a given resource one can adapt it to an InputStream. Most resource providers return the underlying wrapper binary in that case. The handling of arbitrary binary properties is resource provider specific (some expose lazy InputStreams others may not support binary properties via ValueMap at all).

Resource Types

The exact method of setting the resource type for a Resource depends on the actual Resource Provider. For the four main Resource Provider implementations provided by Sling, the assignments are as follows:

Provider Resource Type Resource Super Type
JCR The value of the sling:resourceType property or the primary node type if the property is not set (a namespace separator colon is replaced by a slash, e.g. the nt:file primary node type is mapped to the nt/file resource type The value of the sling:resourceSuperType of the Resource node or resource super type of the resource pointed to by the resource type (when accessed with ResourceResolver.getResource(String)
File System File based resources are of type nt/file; folder based resources are of type nt/folder corresponding to the respective JCR primary node type none
Bundle File based resources are of type nt/file; folder based resources are of type nt/folder corresponding to the respective JCR primary node type none
Servlet The absolute path of the resource appended with the suffix .servlet sling/bundle/resource

Resource Types form a type hierarchy much like Java classes form a type hierarchy. Each resource type has a resource super type, either explicitly defined as for example for JCR or Servlet Resources or implicitly. The implicit Resource Super Type is at the same time the root Resource Type much like the java.lang.Object class is called sling/servlet/default (for historical reasons). The sling/servlet/default Resource Type is the only type without a super type.

Adapters

The object types to which Resources may be adapted mostly depends on the Resource Provider providing the resource. For example all JCR node based resources always adapt to javax.jcr.Node objects.

If the actual Resource object class implementation extends from the SlingAdaptable class, then in addition all AdapterFactory services adapting Resource objects are considered when trying to adapt the Resource. In general Resource Providers are recommended to have their Resource implementation extend from AbstractResource which guarantees the Resource implementation to extend from SlingAdaptable and thus supporting Adapter Factories.

How to get a Resource

To get at Resources, you need a ResourceResolver. This interface defines four kinds of methods to access resources:

  • Absolute Path Mapping Resource Resolution: The resolve(HttpServletRequest, String) and resolve(String) methods are called to apply some implementation specific path matching algorithm to find a Resource. These methods are mainly used to map external paths - such as path components of request URLs - to Resources. To support creating external paths usable in an URL a third method map(String) is defined, which allows for round-tripping.
  • Absolute or Relative Path Resolution (including search path): The getResource(String path) and getResource(Resource base, String path) methods may be used to access a resource with an absolute path directly. If it can't be found the path is assumed to be relative and the search path retrieved from getSearchPath() is used to retrieve the resource. This mechanism is similar to resolving a programm with the PATH environment variable in your favourite operating system.
  • Resource Enumeration: To enumerate resources and thus iterate the resource tree, the listChildren(Resource) method may be used. This method returns an Iterator<Resource> listing all resources whose path prefix is the path of the given Resource. This method will of course also cross boundaries of registered ResourceProvider instances to enable iterating the complete resource tree.
  • Resource Querying: Querying resources is currently only supported for JCR Resources through the findResources(String query, String language) and queryResources(String query, String language) methods. For more information see the section on Querying Resources below.

How to get a ResourceResolver

Usually you get the ResourceResolver via the OSGi service ResourceResolverFactory. This interface provides different methods to create a ResourceResolver:

  1. ResourceResolverFactory.getResourceResolver(java.util.Map authenticationInfo). You must provide some authentication info details for this to work. Those are highly dependent on the implementing ResourceProvider. See sections below for further details which keys are supported.
  2. ResourceResolverFactory.getServiceResourceResolver(java.util.Map authenticationInfo). Can optionally be further parameterized via the authentication info key sling.service.subservice. For further details refer to ServiceAuthentication.
  3. ResourceResolverFactory.getThreadResourceResolver(). Uses the resource resolver bound to the current thread.
  4. The deprecated ResourceResolverFactory.getAdministrativeResourceResolver(...). Instead rather use the method 2.

It is crucial that each ResourceResolver retrieved via one of those methods is closed via ResourceResolver.close() once it is no longer used.

Absolute Path Mapping

As has been said, the absolute path mapping methods resolve(HttpServletRequest, String) and resolve(String) apply some implementation specific path matching algorithm to find a Resource. The difference between the two methods is that the former may take more properties of the HttpServletRequest into account when resolving the Resoure, while the latter just has an absolute path to work on.

The general algorithm of the two methods is as follows:

  1. Call HttpServletRequest.getScheme(), .getServerName(), getServerPort to get an absolute path out of the request URL: [scheme]/[host].[port][path] (resolve(HttpServletRequest, String) method only, which)
  2. Check whether any virtual path matches the absolute path. If such a match exists, the next step is entered with the match.
  3. Apply a list of mappings in order to create a mapped path. The first mapped path resolving to a Resource is assumed success and the Resource found is returned.
  4. If no mapping created a mapped path addressing an existing Resource, the method fails and returns a NonExistingResource (for the resolve(String) and resolve(HttpServletRequest,String)) or null (for the getResource(String path) and getResource(Resource base, String path) methods).

The virtual path mapping may be used to create shortcut URLs for otherwise long and complicated URLs. An example of such an URL might be the main administrative page of a CMS system. So, administrators may access the root of the web application and directed to the main administrative page.

The path mapping functionality may be used to hide internal resource organization from the request URL space. For example to better control the structure of your repository, you might decide to store all accessible data inside a /content subtree. To hide this fact from the users, a mapping may be defined to prefix all incoming paths with /content to get at the actual Resource.

The map(String) applies the path mapping algorithm in the reverse order. That is, first the path mappings are reversed and then any virtual mappings are checked. So, a path /content/sample might be mapped /sample to revers the /content prefixing. Or the main administrative page - say /system/admin/main.html - may be mapped to the virtual URL /.

More details on mappings can be found at Mappings for Resource Resolution.

Relative Path Resolution

Sometimes it is required to resolve relative paths to Resources. An example of such a use case is Script and Servlet resolution which starts with a relative path consisting of the Resource type, optional selectors and the request extension or method name. By scanning a search path for these relative paths a system provided Resource may be overwritten with some user defined implementation.

Consider for example, the system would provide a Servlet to render Resources of type nt:file. This Servlet would be registered under the path /libs/nt/file/html. For a certain web application, this default HTML rendering might not be appropriate, so a Script is created as /apps/nt/file/html.jsp with a customized HTML rendering. By defining the search path to be [/apps,/libs] the Servlet resolver would call the ResourceResolver.getResource(String) method with the relative path nt/file/html and be provided with the first matching resource - /apps/nt/file/html.jsp in this example.

Of course the search path is not used for absolute path arguments.

Querying Resources

For convenience the ResourceResolver provides two Resource querying methods findResources and queryResources both methods take as arguments a JCR query string and a query language name. These parameters match the parameter definition of the QueryManager.createQuery(String statement, String language) method of the JCR API.

The return value of these two methods differ in the use case:

  • findResources returns an Iteratory<Resource> of all Resources matching the query. This method is comparable to calling getNodes() on the QueryResult returned from executing the JCR query.
  • queryResources returns an Iterator<Map<String, Object>>. Each entry in the iterator is a Map<String, Object representing a JCR result Row in the RowIterator returned from executing the JCR query. The map is indexed by the column name and the value of each entry is the value of the named column as a Java Object.

These methods are convenience methods to more easily post queries to the repository and to handle results in very straight forward way using only standard Java functionality.

Please note, that Resource querying is currently only supported for repository based Resources. These query methods are not reflected in the ResourceProvider interface used to inject non-repository Resources into the Resource tree.

Providing Resources

The virtual Resource tree to which the the Resource accessor methods resolve and getResource provide access is implemented by a collection of registered ResourceProvider instances. The main Resource provider is of course the repository based JcrResourceProvider which supports Node and Property based resources. This Resource provider is always available in Sling. Further Resource providers may or may not exist.

Each Resource provider is registered as an OSGi service with a required service registration property provider.roots. This is a multi-value String property listing the absolute paths Resource tree entries serving as roots to provided subtrees. For example, if a Resource provider is registered with the service registration property provider.roots set to /some/root, all paths starting with /some/root are first looked up in the given Resource Provider.

When looking up a Resource in the registered Resource providers, the ResourceResolver applies a longest prefix matching algorithm to find the best match. For example consider three Resource provider registered as follows:

  • JCR Resource provider as /
  • Resource provider R1 as /some
  • Resource provider R2 as /some/path

When accessing a Resource with path /some/path/resource the Resource provider R2 is first asked. If that cannot provide the resource, Resource provider R1 is asked and finally the JCR Resource provider is asked. The first Resource provider having a Resource with the requested path will be used.

JCR-based Resources

JCR-based Resources are provided with the default JcrResourceProvider. This Resource provider is always available and is always asked last. That is Resources provided by other Resource providers may never be overruled by repository based Resources.

These are the authenticationInfo keys (which can be used with ResourceResolverFactory.getResourceResolver(java.util.Map authenticationInfo)) which are supported by the JcrResourceProvider:

AuthenticationInfo Key Constant Type Description
user.jcr.session JcrResourceConstants.AUTHENTICATION_INFO_SESSION javax.jcr.Session The session which is used for the underlying repository access. When calling close() on the returned ResourceResolver the session will not(!) be closed.
user.jcr.credentials JcrResourceConstants.AUTHENTICATION_INFO_CREDENTIALS javax.jcr.Credentials The credentials object from which to create the new underlying JCR session
user.name ResourceResolverFactory.AUTHENTICATION_INFO_CREDENTIALS String Optionally used with user.password to create simple credentials from which the Session is being created.
user.impersonation ResourceResolverFactory.USER_IMPERSONATION String User ID which should be used for impersonation via javax.jcr.Session.impersonate(...). Must be combined with one of the other authentication info keys.

There is support for the following path parameters:

Path Parameter Example Value Description Since
v 1.0 Retrieves the underlying JCR node from the version history leveraging the version label given in the value. SLING-848

Binary Support

Binary properties are exposed as InputStream in the resource's ValueMap. That input stream only needs to be closed in case one reads from it. This prevents always checking the ValueMap for dangling InputStream properties.

The main binary property (i.e. the one being exposed by Resource.adaptTo(InputStream.class)) is determined like follows:

  1. jcr:data is such a property exists, otherwise
  2. the primary item of the underlying node (as defined by Node.getPrimaryItem()).

For node type nt:file the property is looked up in the child node jcr:content for both cases. For all other node types it is looked up in the underlying node of the current resource.

External Binaries

In case when binaries are stored externally (e.g. in a Cloud Storage) accessing those via Sling just induces additional overhead. Therefore with SLING-7140 support for Oak's Direct Binary Access was added. External binaries now expose also as o.a.s.ai.resource.external.ExternalizableInputStream allowing to access the direct streaming URI. This is leveraged e.g. in the default GET servlet's stream rendering.

Resource Metadata

The resource metadata exposed in Resource.getResourceMetadata get their values from JCR properties:

Metadata Property Backed by
sling.contentType JCR property jcr:mimeType
sling.characterEncoding JCR property jcr:encoding
sling.creationTime JCR property jcr:created
sling.modificationTime JCR property jcr:lastModified
sling.contentLength The content length of the JCR property jcr:data or alternatively the primary item of the underlying Node (according to its node type definition)

For binary nodes all properties except jcr:created are retrieved from the child node jcr:content.

ResourceResolver Attributes

The following attributes are exposed from a JCR backed ResourceResolver in its getAttribute(...) and getAttributeNames(...) methods (in addition to the default attributes)

Attribute Name Description
user.name The User ID bound to the current javax.jcr.Session as exposed by getUserID()
all javax.jcr.Session attributes For Oak based repositories those are described in Oak Session Attributes

Bundle-based Resources

Resources may by provided by OSGi bundles. Providing bundles have a Bundle manifest header Sling-Bundle-Resources containing a list of absolute paths provided by the bundle. The path are separated by comma or whitespace (SP, TAB, VTAB, CR, LF).

The BundleResourceProvider supporting bundle-based Resources provides directories as Resources of type nt:folder and files as Resources of type nt:file. This matches the default primary node types intended to be used for directories and files in JCR repositories.

For details see Bundle Resource.

Servlet Resources

Servlet Resources are registered by the Servlet Resolver bundle for Servlets registered as OSGi services. See Servlet Resolution for information on how Servlet Resources are provided.

File System Resources

The Filesystem Resource Provider provides access to the operating system's filesystem through the Sling ResourceResolver. Multiple locations may be mapped into the resource tree by configuring the filesystem location and the resource tree root path for each location to be mapped.

For details see File System Resources.

Merged Resources

The merged resource provider exposes a view on merged resources from multiple locations. For details see Resource Merger. Another approach is taken by the Superimposing Resource Provider.

Custom Resource providers

Custom ResourceProvider services can be used to integrate your own custom resources in the Sling resource tree.

For a simple example of that, see the PlanetResourceProvider used in our integration tests.

Writeable Resources

Sling now supports full CRUD functionality on Resources, without necessarily having to go through the JCR API.

The advantage is that this works for any ResourceProvider that supports the required operations.

See the testSimpleCRUD method in WriteableResourcesTest for a basic example of how that works. More details can be found at Sling API CRUD Support.

Resource Observation

To be notified whenever certain resources or their properties have been modified/added/removed there are different possibilities

Resource Observation API (ResourceChangeListener)

This API is only available since Sling API 2.11.0 (SLING-4751).

Register an OSGi service for org.apache.sling.api.resource.observation.ResourceChangeListener to be notified about local changes. To be also notified about external changes (i.e. changes triggered by another Sling instance leveraging a clustered repository make sure that your service implementation also implements the marker interface org.apache.sling.api.resource.observation.ExternalResourceChangeListener. The interface ExternalResourceChangeListener is not supposed to be registered with OSGi though. It is possible to specify already during registration which events in which paths you are interested in. To use this just set these service properties in your implementing OSGi service (all of them are multi-value strings):

Property Description
ResourceChangeListener.PATHS denote the path (including sub paths) in which you are interested in. This property is required.
ResourceChangeListener.CHANGES the type of changes you are interested in (optional)
ResourceChangeListener.PROPERTY_NAMES_HINT filter only for events affecting the properties with the given names (optional)

OSGi Component Property Type

In order to ease creating ResourceChangeListeners, there is an according OSGi component property type provided through artifact

<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.resource.observation.annotations</artifactId>

It can be used like this:

import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.osgi.service.component.annotations.Component;

@Component
@SlingResourceChangeListener(
        paths = "/examplepath",
        change_types = {ChangeType.ADDED, ChangeType.REMOVED})
public class SampleResourceChangeListener implements ResourceChangeListener {

    @Override
    public void onChange(List<ResourceChange> changes) {
        // Do something here
    }
}

This will generate the necessary service properties automatically (with the help of the Bnd plugin, compare with Sling Servlet Annotations).

OSGi Event based resource changes (deprecated)

Resource events are sent out via the OSGi Event Admin. You can subscribe to those events by registering an OSGi service for org.osgi.service.event.EventHandler. Several properties should be used to restrict the subscription to only the relevant event(s). The event topics which are used for resources are listed as constants in org.apache.sling.api.SlingConstants starting with the prefix TOPIC_.

You receive events no matter whether they originate from the local repository or from a remote clustered repository. You can check though in your event listener for the event attribute event.application, which is only set in case the event was triggered from an external resource modification (compare with DEAConstants and try to reuse the constant from there).

The OSGi event handlers may be blacklisted by Apache Felix in case the processing takes too long. Therefore dispatch all long-lasting operations to a new thread or start a new Sling Job.

This approach is deprecated in favor of the ResourceChangeListener described above, because the the ResourceChangeListeners allows the implementation to send out only relevant events (i.e. those which have subscribers), while the OSGI event based approach needs to create OSGI events for all repository events. To ease the transition the implementation will warn whenever an listener is registered which listens for the Resource (org/apache/sling/api/resource/Resource/*) or ResourceProvider (org/apache/sling/api/resource/ResourceProvider/*) topics.

Wrap/Decorate Resources

The Sling API provides an easy way to wrap or decorate a resource before returning. Details see Wrap or Decorate Resources.

- ( Resources )