001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * docs/licenses/cddl.txt
011 * or http://www.opensource.org/licenses/cddl1.php.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * docs/licenses/cddl.txt.  If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 *      Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 *      Portions Copyright 2010-2023 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.ds.scripting;
028
029
030
031import java.util.List;
032
033import com.unboundid.directory.sdk.common.internal.Reconfigurable;
034import com.unboundid.directory.sdk.common.types.Entry;
035import com.unboundid.directory.sdk.common.types.OperationContext;
036import com.unboundid.directory.sdk.ds.types.DirectoryServerContext;
037import com.unboundid.directory.sdk.ds.config.VirtualAttributeProviderConfig;
038import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
039import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
040import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
041import com.unboundid.ldap.sdk.Attribute;
042import com.unboundid.ldap.sdk.LDAPException;
043import com.unboundid.ldap.sdk.ResultCode;
044import com.unboundid.util.Extensible;
045import com.unboundid.util.ThreadSafety;
046import com.unboundid.util.ThreadSafetyLevel;
047import com.unboundid.util.args.ArgumentException;
048import com.unboundid.util.args.ArgumentParser;
049
050
051
052/**
053 * This class defines an API that must be implemented by scripted extensions
054 * which construct attribute values which may be included in entries instead of
055 * or in addition to real values which are actually stored in the backend.  The
056 * other attributes in the entry will be available for use in the process of
057 * generating the entry, and internal or external operations may also be
058 * performed if the generated values should incorporate data from other
059 * locations.
060 * <BR><BR>
061 * Each virtual attribute provider may be configured to indicate whether the
062 * associated virtual attribute should be included in a given entry.  This
063 * criteria may include the entry's location in the DIT, whether it matches a
064 * given filter, whether it is a member of a specified group, and whether the
065 * requesting client has been assigned a given client connection policy.  This
066 * is handled automatically by the server, so individual virtual attribute
067 * provider implementations do not need to attempt to perform that filtering on
068 * their own.  However, they may perform additional processing if desired to
069 * further narrow the set of entries for which the virtual attribute should be
070 * generated.
071 * <BR><BR>
072 * In addition, virtual attribute providers may be configured to indicate the
073 * behavior that should be exhibited in the event that the target attribute
074 * already exists in the entry with one or more real values.  In this case, the
075 * real values may be used instead of generating virtual values, the virtual
076 * values may be used in place of the real values, or both the real and virtual
077 * values may be merged and presented together.  This work is also automatically
078 * performed by the server, so virtual attribute providers do not need to do any
079 * processing to determine whether to generate a value based on whether the
080 * target attribute already exists in the entry.
081 * <BR><BR>
082 * The server supports multiple virtual attribute providers targeting the same
083 * attribute applying to the same entry. Evaluation order and value selection is
084 * determined by the server based on configuration of the virtual attribute
085 * providers.
086 * <BR>
087 * <H2>Configuring Groovy-Scripted virtual attribute providers</H2>
088 * In order to configure a scripted virtual attribute provider based on this API
089 * and written in the Groovy scripting language, use a command like:
090 * <PRE>
091 *      dsconfig create-virtual-attribute \
092 *           --name "<I>{name}</I>" \
093 *           --type groovy-scripted \
094 *           --set enabled:true \
095 *           --set attribute-type:{attribute} \
096 *           --set "script-class:<I>{class-name}</I>" \
097 *           --set "script-argument:<I>{name=value}</I>"
098 * </PRE>
099 * where "<I>{name}</I>" is the name to use for the virtual attribute provider
100 * instance, "<I>{attribute}</I>", is the name of the attribute for which the
101 * virtual attribute provider should be used to generate values,
102 * "<I>{class-name}</I>" is the fully-qualified name of the Groovy class written
103 * using this API, and "<I>{name=value}</I>" represents name-value pairs for any
104 * arguments to provide to the virtual attribute provider.  If multiple
105 * arguments should be provided to the virtual attribute provider, then the
106 * "<CODE>--set script-argument:<I>{name=value}</I></CODE>" option should be
107 * provided multiple times.
108 *
109 * @see  com.unboundid.directory.sdk.ds.api.VirtualAttributeProvider
110 */
111@Extensible()
112@DirectoryServerExtension()
113@DirectoryProxyServerExtension(appliesToLocalContent=true,
114     appliesToRemoteContent=false)
115@SynchronizationServerExtension(appliesToLocalContent=true,
116     appliesToSynchronizedContent=false)
117@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
118public abstract class ScriptedVirtualAttributeProvider
119       implements Reconfigurable<VirtualAttributeProviderConfig>
120{
121  /**
122   * Creates a new instance of this virtual attribute provider.  All virtual
123   * attribute provider implementations must include a default constructor, but
124   * any initialization should generally be done in the
125   * {@code initializeVirtualAttributeProvider} method.
126   */
127  public ScriptedVirtualAttributeProvider()
128  {
129    // No implementation is required.
130  }
131
132
133
134  /**
135   * {@inheritDoc}
136   */
137  public void defineConfigArguments(final ArgumentParser parser)
138         throws ArgumentException
139  {
140    // No arguments will be allowed by default.
141  }
142
143
144
145  /**
146   * Initializes this virtual attribute provider.
147   *
148   * @param  serverContext  A handle to the server context for the server in
149   *                        which this extension is running.
150   * @param  config         The general configuration for this virtual attribute
151   *                        provider.
152   * @param  parser         The argument parser which has been initialized from
153   *                        the configuration for this virtual attribute
154   *                        provider.
155   *
156   * @throws  LDAPException  If a problem occurs while initializing this virtual
157   *                         attribute provider.
158   */
159  public void initializeVirtualAttributeProvider(
160                   final DirectoryServerContext serverContext,
161                   final VirtualAttributeProviderConfig config,
162                   final ArgumentParser parser)
163         throws LDAPException
164  {
165    // No initialization will be performed by default.
166  }
167
168
169
170  /**
171   * Performs any cleanup which may be necessary when this virtual attribute
172   * provider is to be taken out of service.
173   */
174  public void finalizeVirtualAttributeProvider()
175  {
176    // No implementation is required.
177  }
178
179
180
181  /**
182   * {@inheritDoc}
183   */
184  public boolean isConfigurationAcceptable(
185                      final VirtualAttributeProviderConfig config,
186                      final ArgumentParser parser,
187                      final List<String> unacceptableReasons)
188  {
189    // No extended validation will be performed.
190    return true;
191  }
192
193
194
195  /**
196   * {@inheritDoc}
197   */
198  public ResultCode applyConfiguration(
199                         final VirtualAttributeProviderConfig config,
200                         final ArgumentParser parser,
201                         final List<String> adminActionsRequired,
202                         final List<String> messages)
203  {
204    // By default, no configuration changes will be applied.  If there are any
205    // arguments, then add an admin action message indicating that the extension
206    // needs to be restarted for any changes to take effect.
207    if (! parser.getNamedArguments().isEmpty())
208    {
209      adminActionsRequired.add(
210           "No configuration change has actually been applied.  The new " +
211                "configuration will not take effect until this virtual " +
212                "attribute provider is disabled and re-enabled or until the " +
213                "server is restarted.");
214    }
215
216    return ResultCode.SUCCESS;
217  }
218
219
220
221  /**
222   * Indicates whether the server may cache values generated by this virtual
223   * attribute provider for reuse against the same entry in the course of
224   * processing the same operation.
225   *
226   * @return  {@code true} if the server may cache the value generated by this
227   *          virtual attribute provider for reuse with the same entry in the
228   *          same operation, or {@code false} if not.
229   */
230  public boolean mayCacheInOperation()
231  {
232    return false;
233  }
234
235
236
237  /**
238   * Indicates whether this virtual attribute provider may generate attributes
239   * with multiple values.
240   *
241   * @return  {@code true} if this virtual attribute provider may generate
242   *          attributes with multiple values, or {@code false} if it will only
243   *          generate single-valued attributes.
244   */
245  public abstract boolean isMultiValued();
246
247
248
249  /**
250   * Generates an attribute for inclusion in the provided entry.
251   *
252   * @param  operationContext  The operation context for the operation in
253   *                           progress, if any.  It may be {@code null} if no
254   *                           operation is available.
255   * @param  entry             The entry for which the attribute is to be
256   *                           generated.
257   * @param  attributeName     The name of the attribute to be generated.
258   *
259   * @return  The generated attribute, or {@code null} if no attribute should be
260   *          generated.
261   */
262  public abstract Attribute generateAttribute(
263                                 final OperationContext operationContext,
264                                 final Entry entry, final String attributeName);
265}