Back
2

Retrieving Resources with Apache Sling

May 21, 2021

Sling assumes everything is a resource, so it's important for AEM developers to understand what resources are and how to work with them. In this post, I try to cover the basics and provide an overview for developers who are new to AEM.

To begin, it's helpful to know that Sling maintains a virtual tree of resources (e.g. pages, users, components, etc.). This virtual tree is a merger of contents (i.e. resources) that are made available by resource providers. Resource providers allow access to resources from the JCR Repository and other locations such as databases or bundles. 

Resource Providers

A resource provider allows access to a resource. The main resource provider is the JcrResourceProvider, it supports Node and Property based resources. Each resource provider is registered as an OSGi service and contains a required property called provider.roots. The property value specifies a path within the tree for which the resource provider should be used. For example, let's assume multiple resource providers have been registered. One of the resource providers contains a provider.roots property value of /subtree/path. When a request to access a resource at /subtree/path/my-resource is made, then this resource provider would receive the request before the others because it's provider.roots has the closest match to the path of the resource.

Resource Resolver

The ResourceResolver is responsible for applying a "longest prefix matching algorithm" to find the best matching resource provider for a given resource. Once a resource object is resolved, the ResourceResolver's defined API can be used to do work such as editing or updating the resource. 

There are two ways to obtain a ResourceResolver:

  1. A resource resolver is available to the request processing servlet
  2. A resource resolver can be created with the ResourceResolverFactoryService

Below is psudo code showing how to obtain the resource resolver for each approach. 

ResourceResolver via Request

The code below assumes you are working with Sling Models, the resolver will have the same permissions as the user making the request.

@Model(adaptables = SlingHttpServletRequest.class)
public class MySlingModel {

    @SlingObject
    private ResourceResolver resourceResolver;

    @PostConstruct
    public void init() {
       ...
    }
}

ResourceResolver via ResourceResolverFactory

In the example Sling Model above, the resource resolver was obtained from the request. I did not open it and therefore, do not need to close it. 

Below is the AutoCloseable resource resolver syntax.  The resource resolver is explicity created and would be required to close if not for the try-with-resource syntax.  

try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(…)) {
  ...
}


In the final code example, the resource resolver is opened and needs to closed explicitly. The code below assumes that you have created the WriteService interface and defined a service user with appropriate permissions for your needs (Review the RepoInit post if you have not already).

@Component(immediate = true, service = WriteService.class)
public class WriteServiceImpl implements WriteService {

    @Reference
    ResourceResolverFactory resolverFactory;

    @Override
    public ResourceResolver getResourceResolverObject() throws LoginException {
        Map<String, Object> param = new HashMap<>();
        param.put(ResourceResolverFactory.SUBSERVICE, "WriteService");
        ResourceResolver resolver;

        resolver = resolverFactory.getServiceResourceResolver(param);
        return resolver;
    }
}

 

The WriteService can be imported and used to retrieve the Resource Resolver in other classes.

ResourceResolver resolver = writeService.getResourceResolverObject();

 

Remember to close the resolver when your work is done.

resolver.close();