blob: 69049339af4d2b56f3f5d4c6c6413a7c76beac75 [file] [log] [blame]
/*
* 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 authentication information property indicating that an existing resource resolver is being cloned.
* Providers that receive stateful objects as authentication information must deep-clone those objects
* when this property is present, to avoid inadvertent state sharing with the existing resolver.
* @since 1.2.0
*/
public static final String AUTH_CLONE = "provider.auth.clone";
/**
* 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;
}
}