
org.apache.sling.spi.resource.provider.ResourceProvider Maven / Gradle / Ivy
/*
* 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.SlingException;
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.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.SyntheticResource;
import aQute.bnd.annotation.ConsumerType;
/**
* API for providers of resources. Used by the {@link ResourceResolver} to
* transparently access resources from different locations such as a JCR
* repository, a database, or bundle resources.
*
* 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.
*
* This service is intended to be implemented by providers of resource
* instances on behalf of the {@link ResourceResolver}. It
* is not intended to be used by client applications directly. A resource
* provider implements this service by extending this class.
*
* 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.
*
* 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()} method is called. The provider context
* instance which is passed to the {@link #start(ProviderContext} method
* contains the updated state.
*
* 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 ResourceContext#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 ResourceContext#getProviderState()}
* will return {@code null}.
*
* 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 {
/**
* 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 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(ResourceContext, String, Map)},
* {@link #delete(ResourceContext, 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 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 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(ResourceContext)}
* when it's refreshed itself.
* Boolean service property, default value is 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 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 ResourceResolverFactory#SUBSERVICE} property may also be
* present.
*
* {@link ResourceResolverFactory} implementations must provide this
* property if their implementation of the
* {@link ResourceResolverFactory#getServiceResourceResolver(Map)} method
* use a resource provider factory.
*
* The type of this property, if present, is
* 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 ResourceResolverFactory#getAdministrativeResourceResolver(Map)}.
*/
public static final String AUTH_ADMIN = "provider.auth.admin";
/**
* The resource type be set on resources returned by the
* {@link #listChildren(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(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.
*/
public void update(final long changeSet) {
this.ctx = ctx;
}
/**
* Get the current provider context.
* @return The provider context or {@code null} if the provider is currently
* not used in the resource tree.
*/
protected ProviderContext getProviderContext() {
return this.ctx;
}
/**
* Authenticate against the resource provider.
*
* 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.
*
* If authentication fails a {@code LoginException} must be thrown.
*
* The returned context object grants access to the provided resources with
* privileges assigned to the service provided by the calling bundle.
*
* The authenticationInfo
map will in general contain the same
* information as provided to the respective {@link ResourceResolver}
* method. For
*
* 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 ResourceResolverFactory#SUBSERVICE} might be provided, but the
* {@link #USER} and {@link #PASSWORD} properties provided in the map must
* be ignored.
*
* The {@link #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.
*
* The resource resolver implementation will call the {@link #logout(Object)}
* method once the resource resolver is closed.
*
* @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
* authenticationInfo
.
* @throws LoginException If an error occurs authenticating with the
* provided credential data.
*
* @see Service
* Authentication
*/
@Nonnull public T authenticate(final @Nonnull Map 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(ResourceContext)} can be called to discard changes.
*
* 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 ctx) {
// nothing to do here
}
/**
* Returns true
if this resource provider has not been closed
* yet and can still be used.
*
* This method will never throw an exception
* even after the resource provider has been closed
*
* This method is only called for resource providers which have a state and
* require authentication.
*
* @param ctx The {@link ResolveContext}.
* @return 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 false
.
*/
public boolean isLive(final @Nonnull ResolveContext ctx) {
return true;
}
/**
* Returns the parent resource from this resource provider or 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 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 Resource
instance.
*/
public @CheckForNull Resource getParent(final @Nonnull ResolveContext 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 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 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 Resource
instance.
*/
public abstract @CheckForNull Resource getResource(@Nonnull final ResolveContext ctx,
@Nonnull final String path,
@Nonnull final ResourceContext resourceContext,
@CheckForNull final Resource parent);
/**
* Returns an Iterator
of {@link Resource} objects loaded from
* the children of the given Resource
. The returned {@link Resource} instances
* are attached to the same
* {@link ResourceResolver} as the given parent
resource.
*
* 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.
*
* 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 SyntheticResource} objects whose resource type MUST be set to
* {@link #RESOURCE_TYPE_SYNTHETIC}.
*
* As with {@link #getResource(ResourceResolver, String)} 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 Iterator
of {@link Resource} objects or null
if the resource
* provider has no children for the given resource.
* @throws NullPointerException
* If parent
is null
.
* @throws SlingException
* If any error occurs acquiring the child resource iterator.
*/
public abstract @CheckForNull Iterator listChildren(final @Nonnull ResolveContext ctx, final @Nonnull Resource parent);
/**
* Returns a collection of attribute names whose value can be retrieved
* calling the {@link #getAttribute(ResourceResolver, String)} method.
*
* 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 null
* @throws IllegalStateException if this resource provider has already been
* closed.
*/
public Collection getAttributeNames(final @Nonnull ResolveContext ctx) {
return null;
}
/**
* Returns the value of the given resource provider attribute or null
* if the attribute is not set or not visible (as e.g. security
* sensitive attributes).
*
* 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 null
if the attribute
* is not set or not accessible.
* @throws IllegalStateException
* if this resource provider has already been closed.
*/
public Object getAttribute(final @Nonnull ResolveContext 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(ResourceResolver)} is called.
*
* A resource provider must value {@link ResourceResolver#PROPERTY_RESOURCE_TYPE}
* to set the resource type of a resource.
*
* 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 Resource create(final @Nonnull ResolveContext ctx, final String path, final Map 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(ResourceResolver)} is called.
*
* 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 ctx, final @Nonnull Resource resource)
throws PersistenceException {
throw new PersistenceException("delete is not supported.");
}
/**
* Revert all transient changes: create, delete and updates.
*
* 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 ctx) {
// nothing to do here
}
/**
* Commit all transient changes: create, delete and updates
*
* 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 ctx)
throws PersistenceException {
// nothing to do here
}
/**
* Are there any transient changes?
*
* 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 boolean hasChanges(final @Nonnull ResolveContext 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 getQueryLanguageProvider() {
return null;
}
/**
* Adapts the provider to another type.
*
* Please not that it is explicitly left as an implementation detail whether
* each call to this method with the same type
yields the same
* object or a new object on each call.
*
* 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.
*
* 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 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 adaptTo(final @Nonnull ResolveContext ctx,
final @Nonnull Class type) {
return null;
}
/**
* This method copies the subgraph rooted at, and including, the resource at
* srcAbsPath
to the new location at destAbsPath
and
* adds it as a child node of the resource at destAbsPath
.
*
* Both resources are resources from this provider and the full tree is
* provided by this provider as well.
*
* The resource at destAbsPath
needs to exist, if not a {@code PersistenceException}
* is thrown. If a child resource with the same name already exists at destAbsPath
* a {@code PersistenceException} is thrown.
*
* 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
* 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 ctx,
final String srcAbsPath,
final String destAbsPath) throws PersistenceException {
return false;
}
/**
* This method moves the subgraph rooted at, and including, the resource at
* srcAbsPath
to the new location at destAbsPath
and
* adds it as a child node of the resource at destAbsPath
.
*
* Both resources are resources from this provider and the full tree is
* provided by this provider as well.
*
* The resource at destAbsPath
needs to exist, if not a {@code PersistenceException}
* is thrown. If a child resource with the same name already exists at destAbsPath
* a {@code PersistenceException} is thrown.
*
* 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
* 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 ctx,
final String srcAbsPath,
final String destAbsPath) throws PersistenceException {
return false;
}
}