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}