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