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 2014-2019 Ping Identity Corporation 026 */ 027package com.unboundid.directory.sdk.proxy.api; 028 029 030 031import java.util.Collections; 032import java.util.List; 033import java.util.Map; 034import java.util.Set; 035 036import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider; 037import com.unboundid.directory.sdk.common.internal.Reconfigurable; 038import com.unboundid.directory.sdk.common.internal.UnboundIDExtension; 039import com.unboundid.directory.sdk.common.operation.ExtendedRequest; 040import com.unboundid.directory.sdk.common.types.OperationContext; 041import com.unboundid.directory.sdk.proxy.config. 042 ProxiedExtendedOperationHandlerConfig; 043import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension; 044import com.unboundid.directory.sdk.proxy.types.BackendSet; 045import com.unboundid.directory.sdk.proxy.types.EntryBalancingRequestProcessor; 046import com.unboundid.directory.sdk.proxy.types.ProxyingRequestProcessor; 047import com.unboundid.directory.sdk.proxy.types.ProxyServerContext; 048import com.unboundid.ldap.sdk.ExtendedResult; 049import com.unboundid.ldap.sdk.LDAPException; 050import com.unboundid.ldap.sdk.ResultCode; 051import com.unboundid.util.Extensible; 052import com.unboundid.util.ObjectPair; 053import com.unboundid.util.ThreadSafety; 054import com.unboundid.util.ThreadSafetyLevel; 055import com.unboundid.util.args.ArgumentException; 056import com.unboundid.util.args.ArgumentParser; 057 058 059 060/** 061 * This class defines an API that must be implemented by extensions which are 062 * used to forward extended operations to one or more backend servers. This 063 * API makes it possible to select the backend set(s) to which the operation 064 * should be forwarded, and to aggregate the responses into a single response to 065 * return to the client. 066 * <BR> 067 * <H2>Configuring Proxied Extended Operation Handlers</H2> 068 * In order to configure a proxied extended operation handler created using this 069 * API, use a command like: 070 * <PRE> 071 * dsconfig create-extended-operation-handler \ 072 * --handler-name "<I>{handler-name}</I>" \ 073 * --type third-party-proxied \ 074 * --set enabled:true \ 075 * --set "extension-class:<I>{class-name}</I>" \ 076 * --set "extension-argument:<I>{name=value}</I>" 077 * </PRE> 078 * where "<I>{handler-name}</I>" is the name to use for the proxied extended 079 * operation handler instance, "<I>{class-name}</I>" is the fully-qualified name 080 * of the Java class that extends {@code 081 * com.unboundid.directory.sdk.proxy.api.ProxiedExtendedOperationHandler}, and 082 * "<I>{name=value}</I>" represents name-value pairs for any arguments to 083 * provide to the proxied extended operation handler. If multiple arguments 084 * should be provided to the proxied extended operation handler, then the 085 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be 086 * provided multiple times. 087 */ 088@Extensible() 089@DirectoryProxyServerExtension(appliesToLocalContent=false, 090 appliesToRemoteContent=true) 091@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 092public abstract class ProxiedExtendedOperationHandler 093 implements UnboundIDExtension, 094 Reconfigurable<ProxiedExtendedOperationHandlerConfig>, 095 ExampleUsageProvider 096{ 097 /** 098 * Creates a new instance of this proxied extended operation handler. All 099 * proxied extended operation handler implementations must include a default 100 * constructor, but any initialization should generally be done in the 101 * {@code initializeProxiedExtendedOperationHandler} method. 102 */ 103 public ProxiedExtendedOperationHandler() 104 { 105 // No implementation is required. 106 } 107 108 109 110 /** 111 * {@inheritDoc} 112 */ 113 @Override() 114 public abstract String getExtensionName(); 115 116 117 118 /** 119 * {@inheritDoc} 120 */ 121 @Override() 122 public abstract String[] getExtensionDescription(); 123 124 125 126 /** 127 * {@inheritDoc} 128 */ 129 @Override() 130 public void defineConfigArguments(final ArgumentParser parser) 131 throws ArgumentException 132 { 133 // No arguments will be allowed by default. 134 } 135 136 137 138 /** 139 * Initializes this proxied extended operation handler. 140 * 141 * @param serverContext A handle to the server context for the server in 142 * which this extension is running. 143 * @param config The general configuration for this extended 144 * operation handler. 145 * @param parser The argument parser which has been initialized from 146 * the configuration for this extended operation 147 * handler. 148 * 149 * @throws LDAPException If a problem occurs while initializing this 150 * extended operation handler. 151 */ 152 public void initializeProxiedExtendedOperationHandler( 153 final ProxyServerContext serverContext, 154 final ProxiedExtendedOperationHandlerConfig config, 155 final ArgumentParser parser) 156 throws LDAPException 157 { 158 // No initialization will be performed by default. 159 } 160 161 162 163 /** 164 * {@inheritDoc} 165 */ 166 @Override() 167 public boolean isConfigurationAcceptable( 168 final ProxiedExtendedOperationHandlerConfig config, 169 final ArgumentParser parser, 170 final List<String> unacceptableReasons) 171 { 172 // No extended validation will be performed by default. 173 return true; 174 } 175 176 177 178 /** 179 * {@inheritDoc} 180 */ 181 @Override() 182 public ResultCode applyConfiguration( 183 final ProxiedExtendedOperationHandlerConfig config, 184 final ArgumentParser parser, 185 final List<String> adminActionsRequired, 186 final List<String> messages) 187 { 188 // By default, no configuration changes will be applied. If there are any 189 // arguments, then add an admin action message indicating that the extension 190 // needs to be restarted for any changes to take effect. 191 if (! parser.getNamedArguments().isEmpty()) 192 { 193 adminActionsRequired.add( 194 "No configuration change has actually been applied. The new " + 195 "configuration will not take effect until this proxied " + 196 "extended operation handler is disabled and re-enabled or " + 197 "until the server is restarted."); 198 } 199 200 return ResultCode.SUCCESS; 201 } 202 203 204 205 /** 206 * Performs any cleanup which may be necessary when this proxied extended 207 * operation handler is to be taken out of service. 208 */ 209 public void finalizeProxiedExtendedOperationHandler() 210 { 211 // No implementation is required. 212 } 213 214 215 216 /** 217 * Retrieves the name of the extended operation with the provided OID. 218 * 219 * @param oid The OID of the extended operation for which to retrieve the 220 * corresponding name. 221 * 222 * @return The name of the extended operation with the specified OID, or 223 * {@code null} if the specified OID is not recognized by this 224 * proxied extended operation handler. 225 */ 226 public abstract String getExtendedOperationName(final String oid); 227 228 229 230 /** 231 * Retrieves the OIDs of the extended operation types supported by this 232 * proxied extended operation handler. 233 * 234 * @return The OIDs of the extended operation types supported by this proxied 235 * extended operation handler. It must not be {@code null} or 236 * empty, and the contents of the set returned must not change over 237 * the life of this proxied extended operation handler. 238 */ 239 public abstract Set<String> getSupportedExtensions(); 240 241 242 243 /** 244 * Retrieves the OIDs of any controls supported by this proxied extended 245 * operation handler. 246 * 247 * @return The OIDs of any controls supported by this proxied extended 248 * operation handler. It may be {@code null} or empty if this 249 * proxied extended operation handler does not support any controls. 250 */ 251 public Set<String> getSupportedControls() 252 { 253 return Collections.emptySet(); 254 } 255 256 257 258 /** 259 * Retrieves the OIDs of any features supported by this proxied extended 260 * operation handler that should be advertised in the server root DSE. 261 * 262 * @return The OIDs of any features supported by this proxied extended 263 * operation handler. It may be {@code null} or empty if this 264 * proxied extended operation handler does not support any features. 265 */ 266 public Set<String> getSupportedFeatures() 267 { 268 return Collections.emptySet(); 269 } 270 271 272 273 /** 274 * Selects the entry-balancing backend set(s) to which the provided extended 275 * request should be forwarded. This method will only be invoked for 276 * operations in which the requester's client connection policy includes one 277 * or more subtree views which reference an entry-balancing request processor. 278 * <BR><BR> 279 * This method returns two groups of backend sets, with the first representing 280 * an initial guess (e.g., based on information obtained from the 281 * entry-balancing global index), and the second representing a fallback if 282 * the initial guess was found to be incorrect. 283 * <BR><BR> 284 * If it can be determined that no backend sets associated with the provided 285 * entry-balancing request processor should be used to process the extended 286 * operation, then the object returned may have both elements set to 287 * {@code null} or empty sets. If it is possible to definitively determine 288 * the set of backend sets to which the operation should be forwarded and no 289 * fallback option is required, then the first element of the returned object 290 * should be populated with a non-empty set and the second element should be 291 * {@code null} or empty. 292 * 293 * @param operationContext The operation context for the extended operation. 294 * @param request The extended request to be processed. 295 * @param requestProcessor The entry-balancing request processor in which 296 * the extended operation should be processed. 297 * 298 * @return The set of backend sets to which the request should be forwarded. 299 * It may be {@code null} (or it may be an {@code ObjectPair} with 300 * both elements empty or {@code null}) if the request should not be 301 * forwarded to any backend set for the entry-balancing request 302 * processor. 303 * 304 * @throws LDAPException If the request should not be forwarded to any of 305 * the backend sets for the provided entry-balancing 306 * request processor, and the result contained in the 307 * exception should be used instead. 308 */ 309 public abstract ObjectPair<Set<BackendSet>,Set<BackendSet>> selectBackendSets( 310 final OperationContext operationContext, 311 final ExtendedRequest request, 312 final EntryBalancingRequestProcessor requestProcessor) 313 throws LDAPException; 314 315 316 317 /** 318 * Obtains the extended result that should be used as a result of processing 319 * an operation in one or more entry-balanced backend sets, or throws an 320 * exception to indicate that the request should instead be forwarded to the 321 * fallback server set(s). 322 * <BR><BR> 323 * This method will only be invoked for cases in which the 324 * {@link #selectBackendSets} method indicates that multiple backend sets 325 * should be accessed in the course of processing an extended request. 326 * 327 * @param operationContext The operation context for the extended 328 * operation. 329 * @param request The extended request that was processed. 330 * @param requestProcessor The entry-balancing request processor in which 331 * the extended operation was processed. 332 * @param results A list of the extended results obtained from 333 * processing the operation in the selected set of 334 * backend sets, with each result paired with 335 * information about the backend set from which it 336 * was obtained. 337 * @param fallbackAvailable Indicates whether a fallback group of backend 338 * sets is available and may be used as a second 339 * attempt at processing the operation if the 340 * result from the initial attempt is not 341 * acceptable. 342 * 343 * @return An extended result that represents the merged result from the 344 * provided list of results. It must not be {@code null}. 345 * 346 * @throws LDAPException To indicate that the initial set of results was not 347 * acceptable and that the operation should instead be 348 * forwarded to the fallback group of backend sets. 349 * If no fallback set of results is available, then an 350 * extended result will be generated from the content 351 * of this exception. 352 */ 353 public abstract ExtendedResult mergeEntryBalancedResults( 354 final OperationContext operationContext, 355 final ExtendedRequest request, 356 final EntryBalancingRequestProcessor requestProcessor, 357 final List<ObjectPair<ExtendedResult,BackendSet>> results, 358 final boolean fallbackAvailable) 359 throws LDAPException; 360 361 362 363 /** 364 * Indicates whether the provided extended request should be forwarded to one 365 * of the servers associated with the provided proxying request processor. 366 * Note that this method will not be invoked for proxying request processors 367 * associated with an entry-balancing request processor. 368 * 369 * @param operationContext The operation context for the extended operation. 370 * @param request The extended request to be processed. 371 * @param requestProcessor The proxying request processor for which to 372 * make the determination. 373 * 374 * @return {@code true} if the extended request should be forwarded to one of 375 * the servers associated with the proxying request processor, or 376 * {@code false} if not. 377 * 378 * @throws LDAPException If the request should not be forwarded to a 379 * backend server associated with the proxying request 380 * processor, but the result contained in the 381 * exception should be used instead. 382 */ 383 public abstract boolean shouldForward(final OperationContext operationContext, 384 final ExtendedRequest request, 385 final ProxyingRequestProcessor requestProcessor) 386 throws LDAPException; 387 388 389 390 /** 391 * Creates the final extended result to return to the client from the provided 392 * list of results obtained from the set of entry-balanced and/or proxying 393 * request processors to which the request was forwarded. 394 * 395 * @param operationContext The operation context for the extended 396 * operation. 397 * @param request The extended request that was processed. 398 * @param results The results from all request processors to which 399 * the request was forwarded. It may be empty if 400 * the request was not forwarded to any backend 401 * servers, in which case this method must 402 * construct an appropriate result. It may have 403 * only a single element if the request was only 404 * forwarded to one server, and in many cases it 405 * may be desirable to simply use that result as 406 * the final result. It may also have multiple 407 * elements if the request was forwarded to 408 * multiple backend servers, in which case this 409 * method must determine whether to return one of 410 * them to the client, or to construct a new result 411 * to return instead. 412 * 413 * @return The final extended result to be returned to the client. 414 */ 415 public abstract ExtendedResult createFinalResult( 416 final OperationContext operationContext, 417 final ExtendedRequest request, 418 final List<ExtendedResult> results); 419 420 421 422 /** 423 * {@inheritDoc} 424 */ 425 @Override() 426 public Map<List<String>,String> getExamplesArgumentSets() 427 { 428 return Collections.emptyMap(); 429 } 430}