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.api;
028
029
030
031import java.util.Collections;
032import java.util.List;
033import java.util.Map;
034
035import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
036import com.unboundid.directory.sdk.common.internal.Reconfigurable;
037import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
038import com.unboundid.directory.sdk.common.types.OperationContext;
039import com.unboundid.directory.sdk.common.types.Entry;
040import com.unboundid.directory.sdk.ds.config.VirtualAttributeProviderConfig;
041import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
042import com.unboundid.directory.sdk.ds.scripting.
043            ScriptedVirtualAttributeProvider;
044import com.unboundid.directory.sdk.ds.types.DirectoryServerContext;
045import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
046import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
047import com.unboundid.ldap.sdk.Attribute;
048import com.unboundid.ldap.sdk.LDAPException;
049import com.unboundid.ldap.sdk.ResultCode;
050import com.unboundid.util.Extensible;
051import com.unboundid.util.ThreadSafety;
052import com.unboundid.util.ThreadSafetyLevel;
053import com.unboundid.util.args.ArgumentException;
054import com.unboundid.util.args.ArgumentParser;
055
056
057
058/**
059 * This class defines an API that must be implemented by extensions which
060 * construct attribute values which may be included in entries instead of or
061 * in addition to real values which are actually stored in the backend.  The
062 * other attributes in the entry will be available for use in the process of
063 * generating the entry, and internal or external operations may also be
064 * performed if the generated values should incorporate data from other
065 * locations.
066 * <BR><BR>
067 * Each virtual attribute provider may be configured to indicate whether the
068 * associated virtual attribute should be included in a given entry.  This
069 * criteria may include the entry's location in the DIT, whether it matches a
070 * given filter, whether it is a member of a specified group, and whether the
071 * requesting client has been assigned a given client connection policy.  This
072 * is handled automatically by the server, so individual virtual attribute
073 * provider implementations do not need to attempt to perform that filtering on
074 * their own.  However, they may perform additional processing if desired to
075 * further narrow the set of entries for which the virtual attribute should be
076 * generated.
077 * <BR><BR>
078 * In addition, virtual attribute providers may be configured to indicate the
079 * behavior that should be exhibited in the event that the target attribute
080 * already exists in the entry with one or more real values.  In this case, the
081 * real values may be used instead of generating virtual values, the virtual
082 * values may be used in place of the real values, or both the real and virtual
083 * values may be merged and presented together.  This work is also automatically
084 * performed by the server, so virtual attribute providers do not need to do any
085 * processing to determine whether to generate a value based on whether the
086 * target attribute already exists in the entry.
087 * <BR><BR>
088 * The server supports multiple virtual attribute providers targeting the same
089 * attribute applying to the same entry. Evaluation order and value selection is
090 * determined by the server based on configuration of the virtual attribute
091 * providers.
092 * <BR>
093 * <H2>Configuring Virtual Attribute Providers</H2>
094 * In order to configure a virtual attribute provider created using this API,
095 * use a command like:
096 * <PRE>
097 *      dsconfig create-virtual-attribute \
098 *           --name "<I>{name}</I>" \
099 *           --type third-party \
100 *           --set enabled:true \
101 *           --set attribute-type:{attribute} \
102 *           --set "extension-class:<I>{class-name}</I>" \
103 *           --set "extension-argument:<I>{name=value}</I>"
104 * </PRE>
105 * where "<I>{name}</I>" is the name to use for the virtual attribute provider
106 * instance, "<I>{attribute}</I>" is the name of the attribute for which the
107 * virtual attribute provider should be used to generate values,
108 * "<I>{class-name}</I>" is the fully-qualified name of the Java class that
109 * extends {@code com.unboundid.directory.sdk.ds.api.VirtualAttributeProvider},
110 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to
111 * provide to the virtual attribute provider.  If multiple arguments should be
112 * provided to the virtual attribute provider, then the
113 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
114 * provided multiple times.
115 *
116 * @see  ScriptedVirtualAttributeProvider
117 */
118@Extensible()
119@DirectoryServerExtension()
120@DirectoryProxyServerExtension(appliesToLocalContent=true,
121     appliesToRemoteContent=false)
122@SynchronizationServerExtension(appliesToLocalContent=true,
123     appliesToSynchronizedContent=false)
124@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
125public abstract class VirtualAttributeProvider
126       implements UnboundIDExtension,
127                  Reconfigurable<VirtualAttributeProviderConfig>,
128                  ExampleUsageProvider
129{
130  /**
131   * Creates a new instance of this virtual attribute provider.  All virtual
132   * attribute provider implementations must include a default constructor, but
133   * any initialization should generally be done in the
134   * {@code initializeVirtualAttributeProvider} method.
135   */
136  public VirtualAttributeProvider()
137  {
138    // No implementation is required.  However, we need to reference the
139    // scripted virtual attribute provider so that checkstyle is satisfied with
140    // the import.
141    final ScriptedVirtualAttributeProvider scriptedProvider = null;
142  }
143
144
145
146  /**
147   * {@inheritDoc}
148   */
149  public abstract String getExtensionName();
150
151
152
153  /**
154   * {@inheritDoc}
155   */
156  public abstract String[] getExtensionDescription();
157
158
159
160  /**
161   * {@inheritDoc}
162   */
163  public void defineConfigArguments(final ArgumentParser parser)
164         throws ArgumentException
165  {
166    // No arguments will be allowed by default.
167  }
168
169
170
171  /**
172   * Initializes this virtual attribute provider.
173   *
174   * @param  serverContext  A handle to the server context for the server in
175   *                        which this extension is running.
176   * @param  config         The general configuration for this virtual attribute
177   *                        provider.
178   * @param  parser         The argument parser which has been initialized from
179   *                        the configuration for this virtual attribute
180   *                        provider.
181   *
182   * @throws  LDAPException  If a problem occurs while initializing this virtual
183   *                         attribute provider.
184   */
185  public void initializeVirtualAttributeProvider(
186                   final DirectoryServerContext serverContext,
187                   final VirtualAttributeProviderConfig config,
188                   final ArgumentParser parser)
189         throws LDAPException
190  {
191    // No initialization will be performed by default.
192  }
193
194
195
196  /**
197   * {@inheritDoc}
198   */
199  public boolean isConfigurationAcceptable(
200                      final VirtualAttributeProviderConfig config,
201                      final ArgumentParser parser,
202                      final List<String> unacceptableReasons)
203  {
204    // No extended validation will be performed by default.
205    return true;
206  }
207
208
209
210  /**
211   * {@inheritDoc}
212   */
213  public ResultCode applyConfiguration(
214                         final VirtualAttributeProviderConfig config,
215                         final ArgumentParser parser,
216                         final List<String> adminActionsRequired,
217                         final List<String> messages)
218  {
219    // By default, no configuration changes will be applied.  If there are any
220    // arguments, then add an admin action message indicating that the extension
221    // needs to be restarted for any changes to take effect.
222    if (! parser.getNamedArguments().isEmpty())
223    {
224      adminActionsRequired.add(
225           "No configuration change has actually been applied.  The new " +
226                "configuration will not take effect until this virtual " +
227                "attribute provider is disabled and re-enabled or until the " +
228                "server is restarted.");
229    }
230
231    return ResultCode.SUCCESS;
232  }
233
234
235
236  /**
237   * Performs any cleanup which may be necessary when this virtual attribute
238   * provider is to be taken out of service.
239   */
240  public void finalizeVirtualAttributeProvider()
241  {
242    // No implementation is required.
243  }
244
245
246
247  /**
248   * Indicates whether the server may cache values generated by this virtual
249   * attribute provider for reuse against the same entry in the course of
250   * processing the same operation.
251   *
252   * @return  {@code true} if the server may cache the value generated by this
253   *          virtual attribute provider for reuse with the same entry in the
254   *          same operation, or {@code false} if not.
255   */
256  public boolean mayCacheInOperation()
257  {
258    return false;
259  }
260
261
262
263  /**
264   * Indicates whether this virtual attribute provider may generate attributes
265   * with multiple values.
266   *
267   * @return  {@code true} if this virtual attribute provider may generate
268   *          attributes with multiple values, or {@code false} if it will only
269   *          generate single-valued attributes.
270   */
271  public abstract boolean isMultiValued();
272
273
274
275  /**
276   * Generates an attribute for inclusion in the provided entry.
277   *
278   * @param  operationContext  The operation context for the operation in
279   *                           progress, if any.  It may be {@code null} if no
280   *                           operation is available.
281   * @param  entry             The entry for which the attribute is to be
282   *                           generated.
283   * @param  attributeName     The name of the attribute to be generated.
284   *
285   * @return  The generated attribute, or {@code null} if no attribute should be
286   *          generated.
287   */
288  public abstract Attribute generateAttribute(
289                                 final OperationContext operationContext,
290                                 final Entry entry, final String attributeName);
291
292
293
294  /**
295   * {@inheritDoc}
296   */
297  public Map<List<String>,String> getExamplesArgumentSets()
298  {
299    return Collections.emptyMap();
300  }
301}