/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.sling.spi.resource.provider; import java.util.Collection; import java.util.Iterator; import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import org.apache.sling.api.resource.LoginException; import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceUtil; import org.osgi.annotation.versioning.ConsumerType; /** * API for providers of resources. Used by the {@link org.apache.sling.api.resource.ResourceResolver} to * transparently access resources from different locations such as a JCR * repository, a database, or bundle resources. * <p> * This extension point is defined by an abstract class (in contrast to * an interface) as this allows to add new functionality in new versions * without breaking any implementation. * <p> * This service is intended to be implemented by providers of resource * instances on behalf of the {@link org.apache.sling.api.resource.ResourceResolver}. It * is not intended to be used by client applications directly. A resource * provider implements this service by extending this class. * <p> * A resource provider must set the {@link #PROPERTY_ROOT} property with an * absolute path. This is the mount point of the resource provider. If there * is more than one provider registering for the same root, only the one * with the highest service ranking is used. * <p> * If a provider is used in the resource tree, it gets activated through * a call of the {@link #start(ProviderContext)} method. If the * provider is not used anymore within the resource tree, the * {@link #stop()} method is called. Whenever * information concerning the provider is changed while the provider * is used, the {@link #update(long)} method is called. The provider context * instance which is passed to the {@link #start(ProviderContext)} method * contains the updated state. * <p> * Some resource providers might require (user) authentication. For example * the JCR resource provider uses authenticated sessions. If a provider * requires authentication it must indicate this by setting the service * registration property {@link #PROPERTY_AUTHENTICATE} to either * {@link #AUTHENTICATE_LAZY} or {@link #AUTHENTICATE_REQUIRED}. In these * cases, the resource resolver calls {@link #authenticate(Map)} and on * successful authentication the provider returns a state object for * the current user. This object is passed into the provider with * every method through {@link ResolveContext#getProviderState()}. * If a provider requires authentication, the {@link #logout(Object)} method * is called, when the resource resolver is closed. If the provider * does not set this service property or sets it to {@link #AUTHENTICATE_NO} * the {@link #authenticate(Map)} and {@link #logout(Object)} method * are never called and therefore {@link ResolveContext#getProviderState()} * will return {@code null}. * <p> * Each method gets the {@link ResourceContext} which gives access to * further functionality. * * @since 1.0.0 (Sling API Bundle 2.11.0) */ @ConsumerType public abstract class ResourceProvider<T> { /** * The name of the service registration property containing the root path * of the resources provided by this provider. * If this property is missing, empty or invalid, the provider is ignored. * (value is "provider.root") */ public static final String PROPERTY_ROOT = "provider.root"; /** * Optional service registration property setting a name for the provider. * The name must not be unique. The name in combination with the root * can be used to identify a resource provider. */ public static final String PROPERTY_NAME = "provider.name"; /** * The name of the service registration property containing a boolean * flag indicating if the ResourceAccessSecurity service should be used for * this provider or not. ResourceAccessSecurity should only be used if the * underlying storage does not provide access control * The default for this value is {@code false}. * (value is "provider.useResourceAccessSecurity") */ public static final String PROPERTY_USE_RESOURCE_ACCESS_SECURITY = "provider.useResourceAccessSecurity"; /** * Default value for {@link #PROPERTY_AUTHENTICATE} * @see #PROPERTY_AUTHENTICATE */ public static final String AUTHENTICATE_NO = "no"; /** * Value for {@link #PROPERTY_AUTHENTICATE} indicating authentication is required. * @see #PROPERTY_AUTHENTICATE */ public static final String AUTHENTICATE_REQUIRED = "required"; /** * Value for {@link #PROPERTY_AUTHENTICATE} indicating authentication is needed * and will be done on demand. * @see #PROPERTY_AUTHENTICATE */ public static final String AUTHENTICATE_LAZY = "lazy"; /** * If a resource provider needs the user to be authenticated this property must be set * to either {@link #AUTHENTICATE_LAZY} or {@link #AUTHENTICATE_REQUIRED}. * If it is set to {@link #AUTHENTICATE_REQUIRED}, the {@link #authenticate(Map)} method * is called when the resource resolver is created and only if authentication against * all resource providers marked as required is successful, a resource resolver is * created. Otherwise the creation fails. * If a provider sets this property to {@link #AUTHENTICATE_LAZY}, the authenticate method * is only invoked if a resource from this provider is requested. This might also happen * for traversal or queries. If the authentication fails, resources from this provider * are not accessible. * If this property is not set or set to {@link #AUTHENTICATE_NO}, no authentication * is required for this provider and the {@link #authenticate(Map)} is never invoked. * String service property, default value is {@link #AUTHENTICATE_NO}. * (value is "provider.authenticate") */ public static final String PROPERTY_AUTHENTICATE = "provider.authenticate"; /** * A modifiable resource provider is capable of creating, changing and deleting resources. * This means the methods {@link #create(ResolveContext, String, Map)}, * {@link #delete(ResolveContext, Resource)} and adapting a resource to a modifiable * value map is supported. * If this flag is set to {@code false}, the resource resolver does not take this * provider into account for modifications and modification operations to this provider * always result in an exception. * If this is set to {@code true}, the property {@link ResourceProvider#PROPERTY_AUTHENTICATE} * must require authentication, otherwise this provider registration is considered * invalid and the provider is not used. * Boolean service property, default value is {@code false}. * (value is "provider.modifiable") */ public static final String PROPERTY_MODIFIABLE = "provider.modifiable"; /** * If this flag is set to {@code true}, the resource resolver will use this provider * for the adaptTo() operation. * Boolean service property, default value is {@code false}. * (value is "provider.adaptable") */ public static final String PROPERTY_ADAPTABLE = "provider.adaptable"; /** * If this flag is set to {@code true}, the resource resolver will call {@link #refresh(ResolveContext)} * when it's refreshed itself. * Boolean service property, default value is {@code false}. * (value is "provider.refreshable") */ public static final String PROPERTY_REFRESHABLE = "provider.refreshable"; /** * If this flag is set to {@code true}, the resource resolver will try to get the attribute * names and the attribute values from this provider. * Boolean service property, default value is {@code false}. * (value is "provider.attributable") */ public static final String PROPERTY_ATTRIBUTABLE = "provider.attributable"; /** * The authentication information property referring to the bundle * providing a service for which a resource provider is to be retrieved. If * this property is provided, the * {@link org.apache.sling.api.resource.ResourceResolverFactory#SUBSERVICE} property may also be * present. * <p> * {@link org.apache.sling.api.resource.ResourceResolverFactory} implementations must provide this * property if their implementation of the * {@link org.apache.sling.api.resource.ResourceResolverFactory#getServiceResourceResolver(Map)} method * use a resource provider factory. * <p> * The type of this property, if present, is * {@code org.osgi.framework.Bundle}. */ public static final String AUTH_SERVICE_BUNDLE = "sling.service.bundle"; /** * The authentication information property indicating to use an * administrative login. This property must be set of the resource * resolver is created through {@link org.apache.sling.api.resource.ResourceResolverFactory#getAdministrativeResourceResolver(Map)}. */ public static final String AUTH_ADMIN = "provider.auth.admin"; /** * The resource type be set on resources returned by the * {@link #listChildren(ResolveContext, Resource)} method to enable traversing the * resource * tree down to a deeply nested provided resource which has no concrete * parent hierarchy (value is"sling:syntheticResourceProviderResource"). * * @see #listChildren(ResolveContext, Resource) */ public static final String RESOURCE_TYPE_SYNTHETIC = "sling:syntheticResourceProviderResource"; /** The context for this provider. */ private volatile ProviderContext ctx; /** * With a call to this method, the provider implementation is notified that * it is used in the resource tree. * @param ctx The context for this provider. */ public void start(@Nonnull ProviderContext ctx) { this.ctx = ctx; } /** * With a call to this method, the provider implementation is notified * that it is not used anymore in the resource tree. */ public void stop() { this.ctx = null; } /** * With a call to this method, the provider implementation is notified * that any information regarding the registration of the provider * has changed. For example, observation listeners might have changed. * This method is only called while the provider is used in the resource * tree. * @param changeSet A bit set of provider info that has changed. * @see ProviderContext#OBSERVATION_LISTENER_CHANGED * @see ProviderContext#EXCLUDED_PATHS_CHANGED */ public void update(final long changeSet) { // nothing to do here } /** * Get the current provider context. * @return The provider context or {@code null} if the provider is currently * not used in the resource tree. */ protected @CheckForNull ProviderContext getProviderContext() { return this.ctx; } /** * Authenticate against the resource provider. * <p> * Returns a provider context object if authentication is successful. The * context object is passed to the resource provider in all messages through * the {@link ResourceContext}. A valid implementation might return {@code null} * as the context information. * <p> * If authentication fails a {@code LoginException} must be thrown. * <p> * The returned context object grants access to the provided resources with * privileges assigned to the service provided by the calling bundle. * <p> * The {@code authenticationInfo} map will in general contain the same * information as provided to the respective {@link org.apache.sling.api.resource.ResourceResolver} * method. For * <p> * The provided {@code authenticationInfo} map may be used to provide * additional information such as the {@link #AUTH_SERVICE_BUNDLE}. * If this property is provided, additional information like * {@link org.apache.sling.api.resource.ResourceResolverFactory#SUBSERVICE} might be provided, but the * {@link org.apache.sling.api.resource.ResourceResolverFactory#USER} and * {@link org.apache.sling.api.resource.ResourceResolverFactory#PASSWORD} * properties provided in the map must be ignored. * <p> * The {@link org.apache.sling.api.resource.ResourceResolverFactory#USER_IMPERSONATION} * property is obeyed but requires that the * authenticated user has permission to impersonate as the requested user. * If such permission is missing, a {@code LoginException} is thrown. * <p> * The resource resolver implementation will call the {@link #logout(Object)} * method once the resource resolver is closed. However, if the resource * provider is already unregistered when the resource resolver is closed, * logout can't be called. Therefore the returned state object might * implement {@link java.io.Closeable}. In this case close is called * on the state object. * * @param authenticationInfo A map of further credential information which * may be used by the implementation to parameterize how the * resource resolver is created. * @return A context data object according to the * {@code authenticationInfo}. * @throws LoginException If an error occurs authenticating with the * provided credential data. * * @see <a * href="http://sling.apache.org/documentation/the-sling-engine/service-authentication.html">Service * Authentication</a> */ public @CheckForNull T authenticate(final @Nonnull Map<String, Object> authenticationInfo) throws LoginException { return null; } /** * If the provider requires authentication, this method is called with the state of the user * returned by {@link #authenticate(Map)} once the resource resolver is closed. * * @param state The provider state returned by {@link #authenticate(Map)}. */ public void logout(final @CheckForNull T state) { // do nothing } /** * The provider is updated to reflect the latest state. * Resources which have changes pending are not discarded. * {@link #revert(ResolveContext)} can be called to discard changes. * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_REFRESHABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. */ public void refresh(final @Nonnull ResolveContext<T> ctx) { // nothing to do here } /** * Returns {@code true} if this resource provider has not been closed * yet and can still be used. * <p> * This method will never throw an exception * even after the resource provider has been closed * <p> * This method is only called for resource providers which have a state and * require authentication. * * @param ctx The {@link ResolveContext}. * @return {@code true} if the resource provider has not been closed * yet and is still active.. Once the resource provider has been closed * or is not active anymore, this method returns {@code false}. */ public boolean isLive(final @Nonnull ResolveContext<T> ctx) { return true; } /** * Returns the parent resource from this resource provider or {@code null} if * the resource provider cannot find it. * * The resource provider must not return cached instances for a resource as * the resource resolver will update the resource meta data of the resource * at the end of the resolution process and this meta data might be different * depending on the full path of resource resolution passed into the * resource resolver. * * @param ctx The {@link ResolveContext}. * @param child The child resource. * @return {@code null} if this provider does not have a resource for * the parent. * @throws org.apache.sling.api.SlingException * may be thrown in case of any problem creating the {@code Resource} instance. */ public @CheckForNull Resource getParent(final @Nonnull ResolveContext<T> ctx, final @Nonnull Resource child) { final String parentPath = ResourceUtil.getParent(child.getPath()); if (parentPath == null) { return null; } return this.getResource(ctx, parentPath, ResourceContext.EMPTY_CONTEXT, null); } /** * Returns a resource from this resource provider or {@code null} if * the resource provider cannot find it. The path must have the {@link #PROPERTY_ROOT} * strings as its prefix. * * The resource provider must not return cached instances for a resource as * the resource resolver will update the resource meta data of the resource * at the end of the resolution process and this meta data might be different * depending on the full path of resource resolution passed into the * resource resolver. * * @param ctx The {@link ResolveContext}. * @param path The full path of the resource. * @param resourceContext Additional information for resolving the resource * @param parent Optional parent resource * @return {@code null} If this provider does not have a resource for * the path. * @throws org.apache.sling.api.SlingException * may be thrown in case of any problem creating the {@code Resource} instance. */ public abstract @CheckForNull Resource getResource(@Nonnull final ResolveContext<T> ctx, @Nonnull final String path, @Nonnull final ResourceContext resourceContext, @CheckForNull final Resource parent); /** * Returns an {@code Iterator} of {@link Resource} objects loaded from * the children of the given {@code Resource}. The returned {@link Resource} instances * are attached to the same * {@link org.apache.sling.api.resource.ResourceResolver} as the given {@code parent} resource. * <p> * This method may be called for resource providers whose root path list contains a path such * that the resource path is a * prefix of the list entry. This allows for the enumeration of deeply nested provided resources * for which no actual parent * hierarchy exists. * <p> * The returned iterator may in turn contain resources which do not actually exist but are required * to traverse the resource * tree. Such resources SHOULD be {@link org.apache.sling.api.resource.SyntheticResource} objects whose resource type MUST be set to * {@link #RESOURCE_TYPE_SYNTHETIC}. * * As with {@link #getResource(ResolveContext, String, ResourceContext, Resource)} the returned Resource objects must * not be cached objects. * * @param ctx The {@link ResolveContext}. * @param parent * The {@link Resource Resource} whose children are requested. * @return An {@code Iterator} of {@link Resource} objects or {@code null} if the resource * provider has no children for the given resource. * @throws NullPointerException * If {@code parent} is {@code null}. * @throws org.apache.sling.api.SlingException * If any error occurs acquiring the child resource iterator. */ public abstract @CheckForNull Iterator<Resource> listChildren(final @Nonnull ResolveContext<T> ctx, final @Nonnull Resource parent); /** * Returns a collection of attribute names whose value can be retrieved * calling the {@link #getAttribute(ResolveContext, String)} method. * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_ATTRIBUTABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. * @return A collection of attribute names or {@code null} * @throws IllegalStateException if this resource provider has already been * closed. */ public @CheckForNull Collection<String> getAttributeNames(final @Nonnull ResolveContext<T> ctx) { return null; } /** * Returns the value of the given resource provider attribute or {@code null} * if the attribute is not set or not visible (as e.g. security * sensitive attributes). * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_ATTRIBUTABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. * @param name The name of the attribute to access * @return The value of the attribute or {@code null} if the attribute * is not set or not accessible. * @throws IllegalStateException * if this resource provider has already been closed. */ public @CheckForNull Object getAttribute(final @Nonnull ResolveContext<T> ctx, final @Nonnull String name) { return null; } /** * Create a new resource at the given path. * The new resource is put into the transient space of this provider * until {@link #commit(ResolveContext)} is called. * <p> * A resource provider must value {@link org.apache.sling.api.resource.ResourceResolver#PROPERTY_RESOURCE_TYPE} * to set the resource type of a resource. * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_MODIFIABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. * @param path The resource path. * @param properties Optional properties * @return The new resource. * * @throws PersistenceException If anything fails */ public @Nonnull Resource create(final @Nonnull ResolveContext<T> ctx, final String path, final Map<String, Object> properties) throws PersistenceException { throw new PersistenceException("create is not supported."); } /** * Delete the resource at the given path. * This change is kept in the transient space of this provider * until {@link #commit(ResolveContext)} is called. * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_MODIFIABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. * @param resource The resource to delete. * * @throws PersistenceException If anything fails */ public void delete(final @Nonnull ResolveContext<T> ctx, final @Nonnull Resource resource) throws PersistenceException { throw new PersistenceException("delete is not supported."); } /** * Revert all transient changes: create, delete and updates. * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_MODIFIABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. */ public void revert(final @Nonnull ResolveContext<T> ctx) { // nothing to do here } /** * Commit all transient changes: create, delete and updates * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_MODIFIABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. * @throws PersistenceException If anything fails */ public void commit(final @Nonnull ResolveContext<T> ctx) throws PersistenceException { // nothing to do here } /** * Are there any transient changes? * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_MODIFIABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. * @return {@code true} if there are pending changes. */ public boolean hasChanges(final @Nonnull ResolveContext<T> ctx) { return false; } /** * Get the optional query language provider. * If the provider supports this kind of query, it must return a query provider implementation * if the provider is active. It should not return a query provider if it is not * active. * This method is called for each query, therefore the provider implementation * might cache the provider object. * * @return A query language provider if this resource provider supports this type of querying. */ public @CheckForNull QueryLanguageProvider<T> getQueryLanguageProvider() { return null; } /** * Adapts the provider 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} 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 behavior with respect to returning newly created or not * instance on each call. * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_ADAPTABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. * @param <AdapterType> The generic type to which this resource is adapted to. * @param type The generic type to which this resource is adapted to. * @return The adapter target or {@code null} if the provider cannot * be adapt to the requested type. */ public @CheckForNull <AdapterType> AdapterType adaptTo(final @Nonnull ResolveContext<T> ctx, final @Nonnull Class<AdapterType> type) { return null; } /** * This method copies the subgraph rooted at, and including, the resource at * {@code srcAbsPath} to the new location at {@code destAbsPath} and * adds it as a child node of the resource at {@code destAbsPath}. * <p> * Both resources are resources from this provider and the full tree is * provided by this provider as well. * <p> * The resource at {@code destAbsPath} needs to exist, if not a {@code PersistenceException} * is thrown. If a child resource with the same name already exists at {@code destAbsPath} * a {@code PersistenceException} is thrown. * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_MODIFIABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. * @param srcAbsPath the path of the resource to be copied. * @param destAbsPath the location to which the resource at * {@code srcAbsPath} is to be copied. * @throws PersistenceException If an error occurs. * @return {@code true} if the provider can perform the copy */ public boolean copy(final @Nonnull ResolveContext<T> ctx, final @Nonnull String srcAbsPath, final @Nonnull String destAbsPath) throws PersistenceException { return false; } /** * This method moves the subgraph rooted at, and including, the resource at * {@code srcAbsPath} to the new location at {@code destAbsPath} and * adds it as a child node of the resource at {@code destAbsPath}. * <p> * Both resources are resources from this provider and the full tree is * provided by this provider as well. * <p> * The resource at {@code destAbsPath} needs to exist, if not a {@code PersistenceException} * is thrown. If a child resource with the same name already exists at {@code destAbsPath} * a {@code PersistenceException} is thrown. * <p> * This method is only called if the provider supports this and indicates * it by setting the {@link #PROPERTY_MODIFIABLE} to the value {@code true}. * * @param ctx The {@link ResolveContext}. * @param srcAbsPath the path of the resource to be copied. * @param destAbsPath the location to which the resource at * {@code srcAbsPath} is to be moved. * @throws PersistenceException If an error occurs. * @return {@code true} if the provider can perform the move */ public boolean move(final @Nonnull ResolveContext<T> ctx, final @Nonnull String srcAbsPath, final @Nonnull String destAbsPath) throws PersistenceException { return false; } }