/*
* 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
*
*
* Copyright 2013-2016 UnboundID Corp.
*/
package com.unboundid.directory.sdk.examples;
import com.unboundid.directory.sdk.common.api.VelocityContextProvider;
import com.unboundid.directory.sdk.common.config.VelocityContextProviderConfig;
import com.unboundid.directory.sdk.common.types.InternalConnection;
import com.unboundid.directory.sdk.common.types.ServerContext;
import com.unboundid.directory.sdk.common.types.VelocityContext;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import com.unboundid.util.args.DNArgument;
import com.unboundid.util.args.StringArgument;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* This class provides a simple example of a Velocity context provider that
* exposes a directory entry for use in a Velocity template. This provider takes
* the following configuration arguments:
* <UL>
* <LI>request-attr -- The name of the HTTP request attribute in which this
* provider expects to find a string used as the naming attribute value of
* the entry to retrieve from the directory.</LI>
* <LI>base-dn -- The base DN where this provider expects to find directory
* entries. The value of this attribute is used to form the DN of an
* entry to retrieve from the directory.</LI>
* <LI>naming-attr -- The attribute used as the RDN of requested entries.
* The value of this attribute is used to form the DN of an entry to
* retrieve from the directory.</LI>
* </UL>
* <p/>
* For example if <i>request-attr</i> is 'name' and a client sends a request
* to the server with a query string parameter like:
* <PRE>
* http://example.com:8080/view/entry?name=user.0
* </PRE>
* then if <i>base-dn</i> is 'ou=people,dc=example,dc=com' and
* <i>naming-attr</i> is 'uid' and an entry exists at
* uid=user.0,ou=people,dc=example,dc=com, the corresponding entry will be put
* into the Velocity context making it available to the template resolved by
* the Velocity servlet for the request. The template may have references
* to $entry that will be replaced at rendering time with the LDAP SDK entry
* instance. So for instance the template might print the DN of the entry
* using the reference $entry.DN.
*/
public final class ExampleVelocityContextProvider extends
VelocityContextProvider
{
private static final String ARG_REQ_ATTR = "request-attr";
private static final String ARG_BASE_DN = "base-dn";
private static final String ARG_NAMING_ATTR = "naming-attr";
private ServerContext serverContext;
private volatile DN baseDN;
private volatile String namingAttr;
private volatile String requestAttr;
/**
* Constructs a default instance.
*/
public ExampleVelocityContextProvider()
{
// No implementation required.
}
/**
* Retrieves a human-readable name for this extension.
*
* @return A human-readable name for this extension.
*/
@Override()
public String getExtensionName()
{
return "Example Velocity Context Provider";
}
/**
* Retrieves a human-readable description for this extension. Each element
* of the array that is returned will be considered a separate paragraph in
* generated documentation.
*
* @return A human-readable description for this extension, or {@code null}
* or an empty array if no description should be available.
*/
@Override()
public String[] getExtensionDescription()
{
return new String[] {
"This Velocity context provider serves as an example that may be used " +
"to demonstrate the process for creating a third-party Velocity " +
"context provider. It will obtain an entry from the local server " +
"using an expected request parameter and place the entry into the " +
"Velocity context making it available to templates."
};
}
/**
* Updates the provided argument parser to define any configuration arguments
* which may be used by this Velocity context provider. The argument parser
* may also be updated to define relationships between arguments (e.g., to
* specify required, exclusive, or dependent argument sets).
*
* @param parser The argument parser to be updated with the configuration
* arguments which may be used by this Velocity context
* provider.
* @throws ArgumentException If a problem is encountered while updating the
* provided argument parser.
*/
@Override()
public void defineConfigArguments(final ArgumentParser parser)
throws ArgumentException
{
// Add an argument that allows you to specify an entry's base DN.
Character shortIdentifier = null;
String longIdentifier = ARG_BASE_DN;
boolean required = true;
int maxOccurrences = 1;
String placeholder = "{dn}";
String description = "The base DN of entries.";
parser.addArgument(new DNArgument(shortIdentifier, longIdentifier,
required, maxOccurrences, placeholder, description));
// Add an argument that allows you to specify an entry's naming attribute.
shortIdentifier = null;
longIdentifier = ARG_NAMING_ATTR;
required = true;
maxOccurrences = 1;
placeholder = "{attr}";
description = "The name of the attribute used as the RDN " +
"for entries.";
parser.addArgument(new StringArgument(shortIdentifier, longIdentifier,
required, maxOccurrences, placeholder, description));
// Add an argument that identifies the HTTP request attribute to be used to
// obtain an entry's naming attribute value. The value of this argument
// identifies an entry in the directory by its naming attribute below the
// base DN.
shortIdentifier = null;
longIdentifier = ARG_REQ_ATTR;
required = true;
maxOccurrences = 1;
placeholder = "{attr}";
description = "The name of the HTTP request attribute from which " +
"to obtain an entry's naming attribute value.";
parser.addArgument(new StringArgument(shortIdentifier, longIdentifier,
required, maxOccurrences, placeholder, description));
}
/**
* 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 Velocity
* context provider.
*/
@Override()
public void initializeVelocityContextProvider(
final ServerContext serverContext,
final VelocityContextProviderConfig config,
final ArgumentParser parser)
throws LDAPException
{
this.serverContext = serverContext;
// Get the base DN.
final DNArgument baseDNArg =
(DNArgument) parser.getNamedArgument(ARG_BASE_DN);
baseDN = baseDNArg.getValue();
// Get the naming attribute.
final StringArgument nameAttrArg =
(StringArgument) parser.getNamedArgument(ARG_NAMING_ATTR);
namingAttr = nameAttrArg.getValue();
// Get the request attribute.
final StringArgument reqAttrArg =
(StringArgument) parser.getNamedArgument(ARG_REQ_ATTR);
requestAttr = reqAttrArg.getValue();
}
/**
* Indicates whether the configuration contained in the provided argument
* parser represents a valid configuration for this extension.
*
* @param config The general configuration for this Velocity
* context provider.
* @param parser The argument parser which has been initialized
* with the proposed configuration.
* @param unacceptableReasons A list that can be updated with reasons that
* the proposed configuration is not acceptable.
* @return {@code true} if the proposed configuration is acceptable, or
* {@code false} if not.
*/
@Override()
public boolean isConfigurationAcceptable(
final VelocityContextProviderConfig config,
final ArgumentParser parser,
final List<String> unacceptableReasons)
{
// The argument parser will handle all of the necessary validation, so
// we don't need to do anything here.
return true;
}
/**
* Attempts to apply the configuration contained in the provided argument
* parser.
*
* @param config The general configuration for this Velocity
* context provider.
* @param parser The argument parser which has been
* initialized with the new configuration.
* @param adminActionsRequired A list that can be updated with information
* about any administrative actions that may be
* required before one or more of the
* configuration changes will be applied.
* @param messages A list that can be updated with information
* about the result of applying the new
* configuration.
* @return A result code that provides information about the result of
* attempting to apply the configuration change.
*/
@Override()
public ResultCode applyConfiguration(
final VelocityContextProviderConfig config,
final ArgumentParser parser,
final List<String> adminActionsRequired,
final List<String> messages)
{
// Get the new base DN.
final DNArgument baseDNArg =
(DNArgument) parser.getNamedArgument(ARG_BASE_DN);
baseDN = baseDNArg.getValue();
// Get the new naming attribute.
final StringArgument nameAttrArg =
(StringArgument) parser.getNamedArgument(ARG_NAMING_ATTR);
namingAttr = nameAttrArg.getValue();
// Get the new request attribute.
final StringArgument reqAttrArg =
(StringArgument) parser.getNamedArgument(ARG_REQ_ATTR);
requestAttr = reqAttrArg.getValue();
return ResultCode.SUCCESS;
}
/**
* Performs any cleanup which may be necessary when this Velocity context
* provider is to be taken out of service.
*/
@Override()
public void finalizeVelocityContextProvider()
{
// No implementation required.
}
/**
* Retrieves a map containing examples of configurations that may be used for
* this extension. The map key should be a list of sample arguments, and the
* corresponding value should be a description of the behavior that will be
* exhibited by the extension when used with that configuration.
*
* @return A map containing examples of configurations that may be used for
* this extension. It may be {@code null} or empty if there should
* not be any example argument sets.
*/
@Override()
public Map<List<String>, String> getExamplesArgumentSets()
{
final LinkedHashMap<List<String>, String> exampleMap =
new LinkedHashMap<List<String>, String>(1);
exampleMap.put(
Arrays.asList(ARG_BASE_DN + "=ou=people,dc=example,dc=com"),
"Specify the base DN where entries exist.");
exampleMap.put(
Arrays.asList(ARG_NAMING_ATTR + "=uid"),
"Specify the naming attribute used to identify entries.");
exampleMap.put(
Arrays.asList(ARG_REQ_ATTR + "=entry"),
"Specify the HTTP request attribute used to obtain the entry's" +
" naming attribute value.");
return exampleMap;
}
/**
* Given an HTTP GET request expected to include a request attribute
* with a value that matches the naming attribute of an entry in the
* directory, obtain the entry and add it to the context, making it
* available to templates.
*
* @param context to update.
* @param request for the view implemented by a template.
* @param response to be sent to the request.
*/
@Override
public void handleGet(final VelocityContext context,
final HttpServletRequest request,
final HttpServletResponse response)
{
// Determine the name of the entry being requested from the
// configured request attribute.
String[] values = request.getParameterMap().get(requestAttr);
String name = values != null && values.length > 0 ? values[0] : null;
try
{
if (name == null)
{
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"Expected \'" + requestAttr + "\' parameter.");
}
// First check to see whether the entry has been stored as a result
// of a previous request.
Object o = getNamedObject(name, request);
if (o instanceof Entry)
{
context.put("entry", o);
}
else
{
// The entry has not been previously requested for the particular
// configured scope.
String dn = namingAttr + "=" + name + "," + baseDN.toString();
InternalConnection conn = serverContext.getInternalRootConnection();
try
{
Entry entry = conn.getEntry(dn);
if (entry == null)
{
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"No entry found at " + dn + ".");
}
else
{
context.put("entry", entry);
setNamedObject(name, entry, request);
}
}
catch (LDAPException le)
{
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Error searching for entry " + dn + ".");
}
}
}
catch (IOException ioe)
{
serverContext.debugCaught(ioe);
}
}
}
|