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-2024 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}