
com.unboundid.directory.sdk.common.api.VelocityContextProvider Maven / Gradle / Ivy
Show all versions of server-sdk Show documentation
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* docs/licenses/cddl.txt
* or http://www.opensource.org/licenses/cddl1.php.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* docs/licenses/cddl.txt. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2013-2024 Ping Identity Corporation
*/
package com.unboundid.directory.sdk.common.api;
import com.unboundid.directory.sdk.broker.internal.BrokerExtension;
import com.unboundid.directory.sdk.common.config.VelocityContextProviderConfig;
import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
import com.unboundid.directory.sdk.common.internal.Reconfigurable;
import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
import com.unboundid.directory.sdk.common.types.ServerContext;
import com.unboundid.directory.sdk.common.types.VelocityContext;
import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
import com.unboundid.directory.sdk.metrics.internal.MetricsEngineExtension;
import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.Extensible;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
import static com.unboundid.directory.sdk.common.config
.VelocityContextProviderConfig.ObjectScope;
import static com.unboundid.directory.sdk.common.config
.VelocityContextProviderConfig.ObjectScope.APPLICATION;
import static com.unboundid.directory.sdk.common.config
.VelocityContextProviderConfig.ObjectScope.REQUEST;
import static com.unboundid.directory.sdk.common.config
.VelocityContextProviderConfig.ObjectScope.SESSION;
/**
* This class defines an API that must be implemented by extensions which
* contribute content to server pages authored as Velocity templates.
* These pages are rendered by the Velocity HTTP Servlet Extension included
* with the server. During rendering of a Velocity page, the template is merged
* with a 'context' that provides values for variables, properties, and method
* references in the template.
*
* A context provider can be restricted to contributing content for certain
* pages by specifying one or more included or excluded views names. A view
* name is the URL request path to a template that does not include the
* HTTP servlet extension's base context path, nor a starting path separator.
* So for example if a template was accessible by the URL
* http://localhost:8080/view/path/to/template the view name would be
* 'path/to/template'.
*
* In addition to contributing content for views, a context provider can also
* be configured to add header fields to HTTP responses using the
* request-header configuration property. Header field values specified the
* by a context provider override values for identical fields names for which
* the Velocity HTTP Servlet Extension is configured to add to responses.
*
* Context providers are restricted to servicing requests for the configured
* set of HTTP operations. By default, only the HTTP GET method is enabled
* though this can be changed by updating the http-method configuration
* property. Implementations should abide by the conventions mandated by the
* HTTP method specification. For example, an implementation that handles the
* GET method should be restricted to retrieval only and not introduce any
* persistent side-effects that would change the state of the server.
*
Configuring Velocity Context Providers
* In order to configure a Velocity context provider created using this API,
* use a command like:
*
* dsconfig create-velocity-context-provider \
* --extension-name "{extension}" \
* --provider-name "{provider}" \
* --type third-party \
* --set enabled:true \
* --set "extension-class:{class-name}" \
* --set "extension-argument:{name=value}"
*
* where "{extension}" is the name of the Velocity HTTP servlet
* extension, ("Velocity" by default)
* "{provider}" is the name to use for the Velocity context provider
* instance, "{class-name}" is the fully-qualified name of the Java
* class that extends
* {@code com.unboundid.directory.sdk.common.api.VelocityContextProvider},
* and "{name=value}" represents name-value pairs for any arguments to
* provide to the virtual attribute provider. If multiple arguments should be
* provided to the virtual attribute provider, then the
* "--set extension-argument:{name=value}
" option should be
* provided multiple times.
*/
@Extensible()
@DirectoryServerExtension()
@DirectoryProxyServerExtension(appliesToLocalContent = true,
appliesToRemoteContent = false)
@SynchronizationServerExtension(appliesToLocalContent = true,
appliesToSynchronizedContent = false)
@MetricsEngineExtension()
@BrokerExtension()
@ThreadSafety(level = ThreadSafetyLevel.INTERFACE_THREADSAFE)
public abstract class VelocityContextProvider
implements UnboundIDExtension,
Reconfigurable,
ExampleUsageProvider
{
// The current configuration.
private VelocityContextProviderConfig config;
/**
* {@inheritDoc}
*/
public abstract String getExtensionName();
/**
* {@inheritDoc}
*/
public abstract String[] getExtensionDescription();
/**
* {@inheritDoc}
*/
public void defineConfigArguments(final ArgumentParser parser)
throws ArgumentException
{
// No arguments will be allowed by default.
}
/**
* Initializes this Velocity context provider.
*
* @param serverContext A handle to the server context for the server in
* which this extension is running.
* @param config The general configuration for this Velocity context
* provider.
* @param parser The argument parser which has been initialized from
* the configuration for this Velocity context
* provider.
* @throws LDAPException If a problem occurs while initializing this context
* provider.
*/
public void initializeVelocityContextProvider(
final ServerContext serverContext,
final VelocityContextProviderConfig config,
final ArgumentParser parser)
throws LDAPException
{
this.config = config;
}
/**
* {@inheritDoc}
*/
public boolean isConfigurationAcceptable(
final VelocityContextProviderConfig config,
final ArgumentParser parser,
final List unacceptableReasons)
{
// No extended validation will be performed by default.
return true;
}
/**
* {@inheritDoc}
*/
public ResultCode applyConfiguration(
final VelocityContextProviderConfig config,
final ArgumentParser parser,
final List adminActionsRequired,
final List messages)
{
// By default, no configuration changes will be applied. If there are any
// arguments, then add an admin action message indicating that the extension
// needs to be restarted for any changes to take effect.
if (!parser.getNamedArguments().isEmpty())
{
adminActionsRequired.add(
"No configuration change has actually been applied. The new " +
"configuration will not take effect until this Velocity " +
"context provider is disabled and re-enabled or until the " +
"server is restarted.");
}
return ResultCode.SUCCESS;
}
/**
* Performs any cleanup which may be necessary when this virtual attribute
* provider is to be taken out of service.
*/
public void finalizeVelocityContextProvider()
{
// No implementation is required.
}
/**
* Handle an HTTP DELETE request. Implementations should be idempotent in
* the sense that the side-effects of a single call to this method are the
* same as N > 0 identical requests.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void handleDelete(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
/**
* Handle an HTTP GET request. Implementations should be restricted to
* retrieval operations only, ensuring there are no persistent side-effects
* generated by the server as a result of this method being called.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void handleGet(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
// Do not respond with an error to avoid breaking existing implementations.
}
/**
* Handle an HTTP HEAD request. Implementations should be restricted to
* retrieval operations only, ensuring there are no persistent side-effects
* generated by the server as a result of this method being called.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void handleHead(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
/**
* Handle an HTTP OPTIONS request. Implementations should ensure there
* are no persistent side-effects generated by the server as a result of this
* method being called.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void handleOptions(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
/**
* Handle an HTTP PATCH request.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void handlePatch(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
/**
* Handle an HTTP POST request.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void handlePost(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
// Do not respond with an error to avoid breaking existing implementations.
}
/**
* Handle an HTTP PUT request. Implementations should be idempotent in
* the sense that the side-effects of a single call to this method are the
* same as N > 0 identical requests.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void handlePut(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
/**
* Handle an HTTP TRACE request. Implementations should ensure there
* are no persistent side-effects generated by the server as a result of this
* method being called.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void handleTrace(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
/**
* Handle an HTTP method not already handled by one of the other
* {@code handleXXX} methods. Implementations should check the value
* of the request's {@code getMethod()} method and take whatever action is
* necessary to fulfill the request before updating the context. Unexpected
* HTTP methods should result in the client sending an HTTP 405 Method Not
* Allowed response.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void handleAdditionalMethod(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
/**
* This method is called following a call to one of the {@code handleXXX}
* methods and may be used for logic that is independent of the HTTP operation
* requested. HTTP method-specific code such as request attribute or form
* data processing should be restricted to the appropriate {@code handleXXX}
* method.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to the client.
*
* @throws IOException if the provider has a problem related to processing
* the response such as sending an error to the client.
*/
public void updateContext(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException
{
// No implementation.
}
/**
* Gets an object from the current user session or servlet context
* depending on the object scope currently configured for this provider.
* This method can be used as a convenience for providers the maintain
* a set of context objects for a particular scope.
*
* @param the type of object to return. If an object exists in
* the current scope with the given name but is not of type
* T this method returns {@code null}
*
* @param name of the object.
* @param request current user request.
*
* @return the named object or {@code null} if no object exists by the
* provided name or an input parameter is (@code null}.
*/
protected T getNamedObject(final String name,
final HttpServletRequest request)
{
T object = null;
if (this.config != null)
{
object = getNamedObject(name, request, config.getObjectScope());
}
return object;
}
/**
* Stores an object in current user request, session or servlet context
* depending on the object scope currently configured for this provider.
* This method can be used as a convenience for providers the maintain
* a set of context objects for a particular scope.
*
* @param name of the object.
* @param object to store.
* @param request current user request.
*/
protected void setNamedObject(final String name,
final Object object,
final HttpServletRequest request)
{
if (this.config != null)
{
setNamedObject(name, object, request, config.getObjectScope());
}
}
/**
* Gets an object from the current user session or servlet context
* depending on the object scope currently configured for this provider.
* This method can be used as a convenience for providers the maintain
* a set of context objects for a particular scope.
*
* @param the type of object to return. If an object exists in
* the current scope with the given name but is not of type
* T this method returns {@code null}
*
* @param name of the object.
* @param request current user request.
* @param scope from which to retrieve the object.
* @return the named object or {@code null} if no object exists by the
* provided name or an input parameter is (@code null}.
*/
@SuppressWarnings("unchecked")
public static T getNamedObject(final String name,
final HttpServletRequest request,
final ObjectScope scope)
{
T object = null;
Object o = null;
if (name != null && request != null)
{
if (REQUEST.equals(scope))
{
o = request.getAttribute(name);
}
else if (SESSION.equals(scope))
{
HttpSession session = request.getSession(false);
if (session != null)
{
o = session.getAttribute(name);
}
}
else if (APPLICATION.equals(scope))
{
o = request.getServletContext().getAttribute(name);
}
}
try
{
object = (T) o;
}
catch (ClassCastException cce)
{
// ignore
}
return object;
}
/**
* Stores an object in current user request, session or servlet context
* depending on the object scope currently configured for this provider.
* This method can be used as a convenience for providers the maintain
* a set of context objects for a particular scope.
*
* @param name of the object.
* @param object to store.
* @param request current user request.
* @param scope in which to set the object.
*/
public static void setNamedObject(final String name,
final Object object,
final HttpServletRequest request,
final ObjectScope scope)
{
if (scope != null && request != null && name != null)
{
if (REQUEST.equals(scope))
{
request.setAttribute(name, object);
}
else if (SESSION.equals(scope))
{
HttpSession session = request.getSession(true);
session.setAttribute(name, object);
}
else if (APPLICATION.equals(scope))
{
request.getServletContext().setAttribute(name, object);
}
}
}
}