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 * Copyright 2010-2014 UnboundID Corp.
026 */
027 package com.unboundid.directory.sdk.ds.scripting;
028
029
030
031 import java.util.List;
032
033 import com.unboundid.directory.sdk.common.internal.Reconfigurable;
034 import com.unboundid.directory.sdk.common.types.Entry;
035 import com.unboundid.directory.sdk.common.types.OperationContext;
036 import com.unboundid.directory.sdk.ds.types.DirectoryServerContext;
037 import com.unboundid.directory.sdk.ds.config.VirtualAttributeProviderConfig;
038 import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
039 import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
040 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
041 import com.unboundid.ldap.sdk.Attribute;
042 import com.unboundid.ldap.sdk.LDAPException;
043 import com.unboundid.ldap.sdk.ResultCode;
044 import com.unboundid.util.Extensible;
045 import com.unboundid.util.ThreadSafety;
046 import com.unboundid.util.ThreadSafetyLevel;
047 import com.unboundid.util.args.ArgumentException;
048 import 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)
118 public 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 }