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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 2007-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.core; 028 import org.opends.messages.MessageBuilder; 029 030 import static org.opends.server.core.CoreConstants.*; 031 import static org.opends.server.loggers.AccessLogger.logCompareRequest; 032 import static org.opends.server.loggers.AccessLogger.logCompareResponse; 033 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; 034 import static org.opends.messages.CoreMessages.*; 035 import java.util.ArrayList; 036 import java.util.Iterator; 037 import java.util.List; 038 039 import org.opends.server.api.ClientConnection; 040 import org.opends.server.api.plugin.PluginResult; 041 import org.opends.server.loggers.debug.DebugLogger; 042 import org.opends.server.loggers.debug.DebugTracer; 043 import org.opends.server.protocols.asn1.ASN1OctetString; 044 import org.opends.server.types.*; 045 import org.opends.server.types.operation.PostResponseCompareOperation; 046 import org.opends.server.types.operation.PreParseCompareOperation; 047 import org.opends.server.workflowelement.localbackend. 048 LocalBackendCompareOperation; 049 050 051 /** 052 * This class defines an operation that may be used to determine whether a 053 * specified entry in the Directory Server contains a given attribute-value 054 * pair. 055 */ 056 public class CompareOperationBasis 057 extends AbstractOperation 058 implements PreParseCompareOperation, CompareOperation, 059 Runnable, PostResponseCompareOperation 060 { 061 /** 062 * The tracer object for the debug logger. 063 */ 064 private static final DebugTracer TRACER = DebugLogger.getTracer(); 065 066 // The attribute type for this compare operation. 067 private AttributeType attributeType; 068 069 // The assertion value for the compare operation. 070 private ByteString assertionValue; 071 072 // The raw, unprocessed entry DN as included in the client request. 073 private ByteString rawEntryDN; 074 075 // The DN of the entry for the compare operation. 076 private DN entryDN; 077 078 // The proxied authorization target DN for this operation. 079 private DN proxiedAuthorizationDN; 080 081 // The set of response controls for this compare operation. 082 private List<Control> responseControls; 083 084 // The attribute type for the compare operation. 085 private String rawAttributeType; 086 087 088 089 /** 090 * Creates a new compare operation with the provided information. 091 * 092 * @param clientConnection The client connection with which this operation 093 * is associated. 094 * @param operationID The operation ID for this operation. 095 * @param messageID The message ID of the request with which this 096 * operation is associated. 097 * @param requestControls The set of controls included in the request. 098 * @param rawEntryDN The raw, unprocessed entry DN as provided in the 099 * client request. This may or may not be a valid 100 * DN as no validation will have been performed yet. 101 * @param rawAttributeType The raw attribute type for the compare operation. 102 * @param assertionValue The assertion value for the compare operation. 103 */ 104 public CompareOperationBasis( 105 ClientConnection clientConnection, long operationID, 106 int messageID, List<Control> requestControls, 107 ByteString rawEntryDN, String rawAttributeType, 108 ByteString assertionValue) 109 { 110 super(clientConnection, operationID, messageID, requestControls); 111 112 113 this.rawEntryDN = rawEntryDN; 114 this.rawAttributeType = rawAttributeType; 115 this.assertionValue = assertionValue; 116 117 responseControls = new ArrayList<Control>(); 118 entryDN = null; 119 attributeType = null; 120 cancelRequest = null; 121 proxiedAuthorizationDN = null; 122 } 123 124 125 126 /** 127 * Creates a new compare operation with the provided information. 128 * 129 * @param clientConnection The client connection with which this operation 130 * is associated. 131 * @param operationID The operation ID for this operation. 132 * @param messageID The message ID of the request with which this 133 * operation is associated. 134 * @param requestControls The set of controls included in the request. 135 * @param entryDN The entry DN for this compare operation. 136 * @param attributeType The attribute type for this compare operation. 137 * @param assertionValue The assertion value for the compare operation. 138 */ 139 public CompareOperationBasis( 140 ClientConnection clientConnection, long operationID, 141 int messageID, List<Control> requestControls, 142 DN entryDN, AttributeType attributeType, 143 ByteString assertionValue) 144 { 145 super(clientConnection, operationID, messageID, requestControls); 146 147 148 this.entryDN = entryDN; 149 this.attributeType = attributeType; 150 this.assertionValue = assertionValue; 151 152 responseControls = new ArrayList<Control>(); 153 rawEntryDN = new ASN1OctetString(entryDN.toString()); 154 rawAttributeType = attributeType.getNameOrOID(); 155 cancelRequest = null; 156 proxiedAuthorizationDN = null; 157 } 158 159 160 161 /** 162 * {@inheritDoc} 163 */ 164 public final ByteString getRawEntryDN() 165 { 166 return rawEntryDN; 167 } 168 169 170 171 /** 172 * {@inheritDoc} 173 */ 174 public final void setRawEntryDN(ByteString rawEntryDN) 175 { 176 this.rawEntryDN = rawEntryDN; 177 178 entryDN = null; 179 } 180 181 182 183 /** 184 * {@inheritDoc} 185 */ 186 public final DN getEntryDN() 187 { 188 return entryDN; 189 } 190 191 192 193 /** 194 * {@inheritDoc} 195 */ 196 public final String getRawAttributeType() 197 { 198 return rawAttributeType; 199 } 200 201 202 203 /** 204 * {@inheritDoc} 205 */ 206 public final void setRawAttributeType(String rawAttributeType) 207 { 208 this.rawAttributeType = rawAttributeType; 209 210 attributeType = null; 211 } 212 213 214 215 /** 216 * {@inheritDoc} 217 */ 218 public final AttributeType getAttributeType() 219 { 220 return attributeType; 221 } 222 223 224 225 /** 226 * {@inheritDoc} 227 */ 228 public void setAttributeType(AttributeType attributeType) 229 { 230 this.attributeType = attributeType; 231 } 232 233 234 235 /** 236 * {@inheritDoc} 237 */ 238 public final ByteString getAssertionValue() 239 { 240 return assertionValue; 241 } 242 243 244 245 /** 246 * {@inheritDoc} 247 */ 248 public final void setAssertionValue(ByteString assertionValue) 249 { 250 this.assertionValue = assertionValue; 251 } 252 253 254 255 /** 256 * {@inheritDoc} 257 */ 258 @Override() 259 public final OperationType getOperationType() 260 { 261 // Note that no debugging will be done in this method because it is a likely 262 // candidate for being called by the logging subsystem. 263 264 return OperationType.COMPARE; 265 } 266 267 268 269 /** 270 * {@inheritDoc} 271 */ 272 @Override() 273 public final String[][] getRequestLogElements() 274 { 275 // Note that no debugging will be done in this method because it is a likely 276 // candidate for being called by the logging subsystem. 277 278 return new String[][] 279 { 280 new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) }, 281 new String[] { LOG_ELEMENT_COMPARE_ATTR, rawAttributeType } 282 }; 283 } 284 285 286 287 /** 288 * {@inheritDoc} 289 */ 290 @Override() 291 public final String[][] getResponseLogElements() 292 { 293 // Note that no debugging will be done in this method because it is a likely 294 // candidate for being called by the logging subsystem. 295 296 String resultCode = String.valueOf(getResultCode().getIntValue()); 297 298 String errorMessage; 299 MessageBuilder errorMessageBuffer = getErrorMessage(); 300 if (errorMessageBuffer == null) 301 { 302 errorMessage = null; 303 } 304 else 305 { 306 errorMessage = errorMessageBuffer.toString(); 307 } 308 309 String matchedDNStr; 310 DN matchedDN = getMatchedDN(); 311 if (matchedDN == null) 312 { 313 matchedDNStr = null; 314 } 315 else 316 { 317 matchedDNStr = matchedDN.toString(); 318 } 319 320 String referrals; 321 List<String> referralURLs = getReferralURLs(); 322 if ((referralURLs == null) || referralURLs.isEmpty()) 323 { 324 referrals = null; 325 } 326 else 327 { 328 StringBuilder buffer = new StringBuilder(); 329 Iterator<String> iterator = referralURLs.iterator(); 330 buffer.append(iterator.next()); 331 332 while (iterator.hasNext()) 333 { 334 buffer.append(", "); 335 buffer.append(iterator.next()); 336 } 337 338 referrals = buffer.toString(); 339 } 340 341 String processingTime = 342 String.valueOf(getProcessingTime()); 343 344 return new String[][] 345 { 346 new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, 347 new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, 348 new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, 349 new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, 350 new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } 351 }; 352 } 353 354 355 356 /** 357 * Retrieves the proxied authorization DN for this operation if proxied 358 * authorization has been requested. 359 * 360 * @return The proxied authorization DN for this operation if proxied 361 * authorization has been requested, or {@code null} if proxied 362 * authorization has not been requested. 363 */ 364 public DN getProxiedAuthorizationDN() 365 { 366 return proxiedAuthorizationDN; 367 } 368 369 370 371 /** 372 * {@inheritDoc} 373 */ 374 public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN) 375 { 376 this.proxiedAuthorizationDN = proxiedAuthorizationDN; 377 } 378 379 380 381 /** 382 * {@inheritDoc} 383 */ 384 @Override() 385 public final List<Control> getResponseControls() 386 { 387 return responseControls; 388 } 389 390 391 392 /** 393 * {@inheritDoc} 394 */ 395 @Override() 396 public final void addResponseControl(Control control) 397 { 398 responseControls.add(control); 399 } 400 401 402 403 /** 404 * {@inheritDoc} 405 */ 406 @Override() 407 public final void removeResponseControl(Control control) 408 { 409 responseControls.remove(control); 410 } 411 412 413 414 /** 415 * Performs the work of actually processing this operation. This 416 * should include all processing for the operation, including 417 * invoking plugins, logging messages, performing access control, 418 * managing synchronization, and any other work that might need to 419 * be done in the course of processing. 420 */ 421 public final void run() 422 { 423 setResultCode(ResultCode.UNDEFINED); 424 425 // Start the processing timer. 426 setProcessingStartTime(); 427 428 // Log the compare request message. 429 logCompareRequest(this); 430 431 // Get the plugin config manager that will be used for invoking plugins. 432 PluginConfigManager pluginConfigManager = 433 DirectoryServer.getPluginConfigManager(); 434 435 // This flag is set to true as soon as a workflow has been executed. 436 boolean workflowExecuted = false; 437 438 try 439 { 440 // Check for and handle a request to cancel this operation. 441 checkIfCanceled(false); 442 443 // Invoke the pre-parse compare plugins. 444 PluginResult.PreParse preParseResult = 445 pluginConfigManager.invokePreParseComparePlugins(this); 446 if(!preParseResult.continueProcessing()) 447 { 448 setResultCode(preParseResult.getResultCode()); 449 appendErrorMessage(preParseResult.getErrorMessage()); 450 setMatchedDN(preParseResult.getMatchedDN()); 451 setReferralURLs(preParseResult.getReferralURLs()); 452 return; 453 } 454 455 456 // Check for a request to cancel this operation. 457 checkIfCanceled(false); 458 459 460 // Process the entry DN to convert it from the raw form to the form 461 // required for the rest of the compare processing. 462 try 463 { 464 if (entryDN == null) 465 { 466 entryDN = DN.decode(rawEntryDN); 467 } 468 } 469 catch (DirectoryException de) 470 { 471 if (debugEnabled()) 472 { 473 TRACER.debugCaught(DebugLogLevel.ERROR, de); 474 } 475 476 setResultCode(de.getResultCode()); 477 appendErrorMessage(de.getMessageObject()); 478 479 return; 480 } 481 482 483 // Retrieve the network group registered with the client connection 484 // and get a workflow to process the operation. 485 NetworkGroup ng = getClientConnection().getNetworkGroup(); 486 Workflow workflow = ng.getWorkflowCandidate(entryDN); 487 if (workflow == null) 488 { 489 // We have found no workflow for the requested base DN, just return 490 // a no such entry result code and stop the processing. 491 updateOperationErrMsgAndResCode(); 492 return; 493 } 494 workflow.execute(this); 495 workflowExecuted = true; 496 497 } 498 catch(CanceledOperationException coe) 499 { 500 if (debugEnabled()) 501 { 502 TRACER.debugCaught(DebugLogLevel.ERROR, coe); 503 } 504 505 setResultCode(ResultCode.CANCELED); 506 cancelResult = new CancelResult(ResultCode.CANCELED, null); 507 508 appendErrorMessage(coe.getCancelRequest().getCancelReason()); 509 } 510 finally 511 { 512 // Stop the processing timer. 513 setProcessingStopTime(); 514 515 if(cancelRequest == null || cancelResult == null || 516 cancelResult.getResultCode() != ResultCode.CANCELED || 517 cancelRequest.notifyOriginalRequestor() || 518 DirectoryServer.notifyAbandonedOperations()) 519 { 520 clientConnection.sendResponse(this); 521 } 522 523 524 // Log the compare response message. 525 logCompareResponse(this); 526 527 // Invoke the post-response compare plugins. 528 invokePostResponsePlugins(workflowExecuted); 529 530 // If no cancel result, set it 531 if(cancelResult == null) 532 { 533 cancelResult = new CancelResult(ResultCode.TOO_LATE, null); 534 } 535 } 536 } 537 538 539 /** 540 * Invokes the post response plugins. If a workflow has been executed 541 * then invoke the post response plugins provided by the workflow 542 * elements of the worklfow, otherwise invoke the post reponse plugins 543 * that have been registered with the current operation. 544 * 545 * @param workflowExecuted <code>true</code> if a workflow has been 546 * executed 547 */ 548 private void invokePostResponsePlugins(boolean workflowExecuted) 549 { 550 // Get the plugin config manager that will be used for invoking plugins. 551 PluginConfigManager pluginConfigManager = 552 DirectoryServer.getPluginConfigManager(); 553 554 // Invoke the post response plugins 555 if (workflowExecuted) 556 { 557 // Invoke the post response plugins that have been registered by 558 // the workflow elements 559 List localOperations = 560 (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); 561 562 if (localOperations != null) 563 { 564 for (Object localOp : localOperations) 565 { 566 LocalBackendCompareOperation localOperation = 567 (LocalBackendCompareOperation)localOp; 568 pluginConfigManager.invokePostResponseComparePlugins(localOperation); 569 } 570 } 571 } 572 else 573 { 574 // Invoke the post response plugins that have been registered with 575 // the current operation 576 pluginConfigManager.invokePostResponseComparePlugins(this); 577 } 578 } 579 580 581 /** 582 * Updates the error message and the result code of the operation. 583 * 584 * This method is called because no workflow was found to process 585 * the operation. 586 */ 587 private void updateOperationErrMsgAndResCode() 588 { 589 setResultCode(ResultCode.NO_SUCH_OBJECT); 590 appendErrorMessage( 591 ERR_COMPARE_NO_SUCH_ENTRY.get(String.valueOf(getEntryDN()))); 592 } 593 594 595 596 /** 597 * {@inheritDoc} 598 */ 599 @Override() 600 public final void toString(StringBuilder buffer) 601 { 602 buffer.append("CompareOperation(connID="); 603 buffer.append(clientConnection.getConnectionID()); 604 buffer.append(", opID="); 605 buffer.append(operationID); 606 buffer.append(", dn="); 607 buffer.append(rawEntryDN); 608 buffer.append(", attr="); 609 buffer.append(rawAttributeType); 610 buffer.append(")"); 611 } 612 613 614 /** 615 * {@inheritDoc} 616 * 617 * This method always returns null. 618 */ 619 public Entry getEntryToCompare() 620 { 621 return null; 622 } 623 624 }