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.math.BigInteger; 032import java.util.List; 033import java.util.Set; 034 035import com.unboundid.directory.sdk.common.internal.Reconfigurable; 036import com.unboundid.directory.sdk.common.operation.AddRequest; 037import com.unboundid.directory.sdk.common.operation.AddResult; 038import com.unboundid.directory.sdk.common.operation.DeleteRequest; 039import com.unboundid.directory.sdk.common.operation.DeleteResult; 040import com.unboundid.directory.sdk.common.operation.ModifyRequest; 041import com.unboundid.directory.sdk.common.operation.ModifyResult; 042import com.unboundid.directory.sdk.common.operation.ModifyDNRequest; 043import com.unboundid.directory.sdk.common.operation.ModifyDNResult; 044import com.unboundid.directory.sdk.common.types.CompletedOperationContext; 045import com.unboundid.directory.sdk.common.types.Entry; 046import com.unboundid.directory.sdk.ds.config.ChangeSubscriptionHandlerConfig; 047import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension; 048import com.unboundid.directory.sdk.ds.types.ChangeSubscription; 049import com.unboundid.directory.sdk.ds.types.DirectoryServerContext; 050import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension; 051import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension; 052import com.unboundid.ldap.sdk.LDAPException; 053import com.unboundid.ldap.sdk.ResultCode; 054import com.unboundid.util.Extensible; 055import com.unboundid.util.ThreadSafety; 056import com.unboundid.util.ThreadSafetyLevel; 057import com.unboundid.util.args.ArgumentException; 058import com.unboundid.util.args.ArgumentParser; 059 060 061 062/** 063 * This class defines an API that must be implemented by scripted extensions 064 * which receive notification of changes of interest processed within the 065 * server. The server may be configured with one or more change subscriptions, 066 * which use criteria to identify the types of changes that should be made 067 * available to change subscription handlers. Each change subscription handler 068 * may be configured either to be notified only for changes matching a specific 069 * set of change subscriptions, or for changes matching the criteria for any 070 * subscription defined in the server. This is handled automatically by the 071 * server, so individual change subscription handler implementations do not need 072 * to attempt to perform that filtering on their own. However, they may perform 073 * additional processing if desired to further narrow the set of changes to be 074 * processed. 075 * <BR> 076 * <H2>Configuring Groovy-Scripted Change Subscription Handlers</H2> 077 * In order to configure a scripted change subscription handler based on this 078 * API and written in the Groovy scripting language, use a command like: 079 * <PRE> 080 * dsconfig create-change-subscription-handler \ 081 * --handler-name "<I>{handler-name}</I>" \ 082 * --type groovy-scripted \ 083 * --set enabled:true \ 084 * --set "script-class:<I>{class-name}</I>" \ 085 * --set "script-argument:<I>{name=value}</I>" 086 * </PRE> 087 * where "<I>{handler-name}</I>" is the name to use for the change subscription 088 * handler instance, "<I>{class-name}</I>" is the fully-qualified name of the 089 * Groovy class written using this API, and "<I>{name=value}</I>" represents 090 * name-value pairs for any arguments to provide to the change subscription 091 * handler. If multiple arguments should be provided to the change subscription 092 * handler, then the "<CODE>--set script-argument:<I>{name=value}</I></CODE>" 093 * option should be provided multiple times. 094 * 095 * @see com.unboundid.directory.sdk.ds.api.ChangeSubscriptionHandler 096 */ 097@Extensible() 098@DirectoryServerExtension() 099@DirectoryProxyServerExtension(appliesToLocalContent=true, 100 appliesToRemoteContent=false) 101@SynchronizationServerExtension(appliesToLocalContent=true, 102 appliesToSynchronizedContent=false) 103@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 104public abstract class ScriptedChangeSubscriptionHandler 105 implements Reconfigurable<ChangeSubscriptionHandlerConfig> 106{ 107 /** 108 * Creates a new instance of this change subscription handler. All change 109 * subscription handler implementations must include a default constructor, 110 * but any initialization should generally be done in the 111 * {@code initializeChangeSubscriptionHandler} method. 112 */ 113 public ScriptedChangeSubscriptionHandler() 114 { 115 // No implementation is required. 116 } 117 118 119 120 /** 121 * {@inheritDoc} 122 */ 123 public void defineConfigArguments(final ArgumentParser parser) 124 throws ArgumentException 125 { 126 // No arguments will be allowed by default. 127 } 128 129 130 131 /** 132 * Initializes this change subscription handler. 133 * 134 * @param serverContext A handle to the server context for the server in 135 * which this extension is running. 136 * @param config The general configuration for this change 137 * subscription handler. 138 * @param parser The argument parser which has been initialized from 139 * the configuration for this change subscription 140 * handler. 141 * 142 * @throws LDAPException If a problem occurs while initializing this change 143 * subscription handler. 144 */ 145 public void initializeChangeSubscriptionHandler( 146 final DirectoryServerContext serverContext, 147 final ChangeSubscriptionHandlerConfig config, 148 final ArgumentParser parser) 149 throws LDAPException 150 { 151 // No initialization will be performed by default. 152 } 153 154 155 156 /** 157 * Performs any cleanup which may be necessary when this change subscription 158 * handler is to be taken out of service. 159 */ 160 public void finalizeChangeSubscriptionHandler() 161 { 162 // No implementation is required. 163 } 164 165 166 167 /** 168 * {@inheritDoc} 169 */ 170 public boolean isConfigurationAcceptable( 171 final ChangeSubscriptionHandlerConfig config, 172 final ArgumentParser parser, 173 final List<String> unacceptableReasons) 174 { 175 // No extended validation will be performed. 176 return true; 177 } 178 179 180 181 /** 182 * {@inheritDoc} 183 */ 184 public ResultCode applyConfiguration( 185 final ChangeSubscriptionHandlerConfig config, 186 final ArgumentParser parser, 187 final List<String> adminActionsRequired, 188 final List<String> messages) 189 { 190 // By default, no configuration changes will be applied. If there are any 191 // arguments, then add an admin action message indicating that the extension 192 // needs to be restarted for any changes to take effect. 193 if (! parser.getNamedArguments().isEmpty()) 194 { 195 adminActionsRequired.add( 196 "No configuration change has actually been applied. The new " + 197 "configuration will not take effect until this change " + 198 "subscription handler is disabled and re-enabled or until " + 199 "the server is restarted."); 200 } 201 202 return ResultCode.SUCCESS; 203 } 204 205 206 207 /** 208 * Performs any processing necessary for an add operation matching the 209 * subscription criteria. 210 * 211 * @param operationContext The context for the add operation. 212 * @param sequenceNumber The sequence number for the change 213 * subscription notification. 214 * @param changeSubscriptions The set of change subscriptions whose criteria 215 * matched the add operation. 216 * @param addRequest Information about the request for the add 217 * operation that was processed. 218 * @param addResult Information about the result for the add 219 * operation that was processed. 220 * @param entry The entry that was added to the server. 221 */ 222 public abstract void addOperationProcessed( 223 final CompletedOperationContext operationContext, 224 final BigInteger sequenceNumber, 225 final Set<ChangeSubscription> changeSubscriptions, 226 final AddRequest addRequest, 227 final AddResult addResult, final Entry entry); 228 229 230 231 /** 232 * Performs any processing necessary for a delete operation matching the 233 * subscription criteria. 234 * 235 * @param operationContext The context for the delete operation. 236 * @param sequenceNumber The sequence number for the change 237 * subscription notification. 238 * @param changeSubscriptions The set of change subscriptions whose criteria 239 * matched the delete operation. 240 * @param deleteRequest Information about the request for the delete 241 * operation that was processed. 242 * @param deleteResult Information about the result for the delete 243 * operation that was processed. 244 * @param entry The entry that was removed from the server. 245 */ 246 public abstract void deleteOperationProcessed( 247 final CompletedOperationContext operationContext, 248 final BigInteger sequenceNumber, 249 final Set<ChangeSubscription> changeSubscriptions, 250 final DeleteRequest deleteRequest, 251 final DeleteResult deleteResult, 252 final Entry entry); 253 254 255 256 /** 257 * Performs any processing necessary for a modify operation matching the 258 * subscription criteria. 259 * 260 * @param operationContext The context for the modify operation. 261 * @param sequenceNumber The sequence number for the change 262 * subscription notification. 263 * @param changeSubscriptions The set of change subscriptions whose criteria 264 * matched the modify operation. 265 * @param modifyRequest Information about the request for the modify 266 * operation that was processed. 267 * @param modifyResult Information about the result for the modify 268 * operation that was processed. 269 * @param oldEntry The entry as it appeared before the changes 270 * were applied. 271 * @param newEntry The entry as it appeared immediately after the 272 * changes were applied. 273 */ 274 public abstract void modifyOperationProcessed( 275 final CompletedOperationContext operationContext, 276 final BigInteger sequenceNumber, 277 final Set<ChangeSubscription> changeSubscriptions, 278 final ModifyRequest modifyRequest, 279 final ModifyResult modifyResult, 280 final Entry oldEntry, final Entry newEntry); 281 282 283 284 /** 285 * Performs any processing necessary for a modify DN operation matching the 286 * subscription criteria. 287 * 288 * @param operationContext The context for the modify DN operation. 289 * @param sequenceNumber The sequence number for the change 290 * subscription notification. 291 * @param changeSubscriptions The set of change subscriptions whose criteria 292 * matched the modify DN operation. 293 * @param modifyDNRequest Information about the request for the modify 294 * DN operation that was processed. 295 * @param modifyDNResult Information about the result for the modify DN 296 * operation that was processed. 297 * @param oldEntry The entry as it appeared before being renamed. 298 * @param newEntry The entry as it appeared immediately after 299 * being renamed. 300 */ 301 public abstract void modifyDNOperationProcessed( 302 final CompletedOperationContext operationContext, 303 final BigInteger sequenceNumber, 304 final Set<ChangeSubscription> changeSubscriptions, 305 final ModifyDNRequest modifyDNRequest, 306 final ModifyDNResult modifyDNResult, 307 final Entry oldEntry, final Entry newEntry); 308}