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 029 030 import java.util.ArrayList; 031 032 import org.opends.server.types.*; 033 import org.opends.server.workflowelement.WorkflowElement; 034 035 036 /** 037 * This class implements a workflow node. A workflow node is used 038 * to build a tree of workflows (aka workflow topology). Each node 039 * may have a parent node and/or subordinate nodes. A node with no 040 * parent is a naming context. 041 * 042 * Each node in the workflow topology is linked to a WorkflowImpl 043 * which contains the real processing. The base DN of the workflow 044 * node is the base DN of the related WorkflowImpl. 045 * 046 * How the workflow topology is built? 047 * A workflow node is a subordinate of another workflow node when 048 * the base DN of the former workflow is an ancestor of the base DN 049 * of the latter workflow. 050 * 051 * A subtree search on a workflow node is performed on the node itself as 052 * well as on all the subordinate nodes. 053 */ 054 public class WorkflowTopologyNode extends WorkflowTopology 055 { 056 // Parent node of the current workflow node. 057 private WorkflowTopologyNode parent = null; 058 059 060 // The list of subordinate nodes of the current workflow node. 061 private ArrayList<WorkflowTopologyNode> subordinates = 062 new ArrayList<WorkflowTopologyNode>(); 063 064 065 /** 066 * Creates a new node for a workflow topology. The new node is initialized 067 * with a WorkflowImpl which contains the real processing. Optionally, 068 * the node may have tasks to be executed before and/or after the real 069 * processing. In the current implementation, such pre and post workflow 070 * elements are not used. 071 * 072 * @param workflowImpl the real processing attached to the node 073 * @param preWorkflowElements the list of tasks to be executed before 074 * the real processing 075 * @param postWorkflowElements the list of tasks to be executed after 076 * the real processing 077 */ 078 public WorkflowTopologyNode( 079 WorkflowImpl workflowImpl, 080 WorkflowElement[] preWorkflowElements, 081 WorkflowElement[] postWorkflowElements 082 ) 083 { 084 super(workflowImpl); 085 } 086 087 088 /** 089 * Executes an operation on a set of data being identified by the 090 * workflow node base DN. 091 * 092 * @param operation the operation to execute 093 * 094 * @throws CanceledOperationException if this operation should 095 * be cancelled. 096 */ 097 public void execute(Operation operation) 098 throws CanceledOperationException { 099 // Execute the operation 100 getWorkflowImpl().execute(operation); 101 102 // For subtree search operation we need to go through the subordinate 103 // nodes. 104 if (operation.getOperationType() == OperationType.SEARCH) 105 { 106 executeSearchOnSubordinates((SearchOperation) operation); 107 } 108 } 109 110 111 /** 112 * Executes a search operation on the subordinate workflows. 113 * 114 * @param searchOp the search operation to execute 115 * 116 * @throws CanceledOperationException if this operation should 117 * be cancelled. 118 */ 119 private void executeSearchOnSubordinates(SearchOperation searchOp) 120 throws CanceledOperationException { 121 // If the scope of the search is 'base' then it's useless to search 122 // in the subordinate workflows. 123 SearchScope originalScope = searchOp.getScope(); 124 if (originalScope == SearchScope.BASE_OBJECT) 125 { 126 return; 127 } 128 129 // Elaborate the new search scope before executing the search operation 130 // in the subordinate workflows. 131 SearchScope newScope = elaborateScopeForSearchInSubordinates(originalScope); 132 searchOp.setScope(newScope); 133 134 // Let's search in the subordinate workflows. 135 WorkflowResultCode workflowResultCode = new WorkflowResultCode( 136 searchOp.getResultCode(), searchOp.getErrorMessage()); 137 DN originalBaseDN = searchOp.getBaseDN(); 138 for (WorkflowTopologyNode subordinate: getSubordinates()) 139 { 140 // We have to change the operation request base DN to match the 141 // subordinate workflow base DN. Otherwise the workflow will 142 // return a no such entry result code as the operation request 143 // base DN is a superior of the subordinate workflow base DN. 144 DN subordinateDN = subordinate.getBaseDN(); 145 146 // If the new search scope is 'base' and the search base DN does not 147 // map the subordinate workflow then skip the subordinate workflow. 148 if ((newScope == SearchScope.BASE_OBJECT) 149 && !subordinateDN.getParent().equals(originalBaseDN)) 150 { 151 continue; 152 } 153 154 // If the request base DN is not a subordinate of the subordinate 155 // worklfow base DN then don't search in the subordinate workflow. 156 if (! originalBaseDN.isAncestorOf(subordinateDN)) 157 { 158 continue; 159 } 160 161 // Set the new request base DN and do execute the 162 // operation in the subordinate workflow. 163 searchOp.setBaseDN(subordinateDN); 164 subordinate.execute(searchOp); 165 boolean sendReferenceEntry = 166 workflowResultCode.elaborateGlobalResultCode( 167 searchOp.getResultCode(), searchOp.getErrorMessage()); 168 if (sendReferenceEntry) 169 { 170 // TODO jdemendi - turn a referral result code into a reference entry 171 // and send the reference entry to the client application 172 } 173 } 174 175 // Now we are done with the operation, let's restore the original 176 // base DN and search scope in the operation. 177 searchOp.setBaseDN(originalBaseDN); 178 searchOp.setScope(originalScope); 179 180 // Update the operation result code and error message 181 searchOp.setResultCode(workflowResultCode.resultCode()); 182 searchOp.setErrorMessage(workflowResultCode.errorMessage()); 183 } 184 185 186 /** 187 * Sets the parent workflow. 188 * 189 * @param parent the parent workflow of the current workflow 190 */ 191 public void setParent(WorkflowTopologyNode parent) 192 { 193 this.parent = parent; 194 } 195 196 197 /** 198 * Gets the parent workflow. 199 * 200 * @return the parent workflow. 201 */ 202 public WorkflowTopologyNode getParent() 203 { 204 return parent; 205 } 206 207 208 /** 209 * Indicates whether the root workflow element is encapsulating a private 210 * local backend or not. 211 * 212 * @return <code>true</code> if the root workflow element encapsulates 213 * a private local backend 214 */ 215 public boolean isPrivate() 216 { 217 return getWorkflowImpl().isPrivate(); 218 } 219 220 221 /** 222 * Gets the base DN of the workflow that handles a given dn. The elected 223 * workflow may be the current workflow or one of its subordiante workflows. 224 * 225 * @param dn the DN for which we are looking a parent DN 226 * @return the base DN which is the parent of the <code>dn</code>, 227 * <code>null</code> if no parent DN was found 228 */ 229 public DN getParentBaseDN(DN dn) 230 { 231 if (dn == null) 232 { 233 return null; 234 } 235 236 // parent base DN to return 237 DN parentBaseDN = null; 238 239 // Is the dn a subordinate of the current base DN? 240 DN curBaseDN = getBaseDN(); 241 if (curBaseDN != null) 242 { 243 if (dn.isDescendantOf(curBaseDN)) 244 { 245 // The dn may be handled by the current workflow. 246 // Now we have to check whether the dn is handled by 247 // a subordinate. 248 for (WorkflowTopologyNode subordinate: getSubordinates()) 249 { 250 parentBaseDN = subordinate.getParentBaseDN(dn); 251 if (parentBaseDN != null) 252 { 253 // the dn is handled by a subordinate 254 break; 255 } 256 } 257 258 // If the dn is not handled by any subordinate, then it is 259 // handled by the current workflow. 260 if (parentBaseDN == null) 261 { 262 parentBaseDN = curBaseDN; 263 } 264 } 265 } 266 267 return parentBaseDN; 268 } 269 270 271 /** 272 * Adds a workflow to the list of workflow subordinates without 273 * additional control. 274 * 275 * @param newWorkflow the workflow to add to the subordinate list 276 * @param parentWorkflow the parent workflow of the new workflow 277 */ 278 private void addSubordinateNoCheck( 279 WorkflowTopologyNode newWorkflow, 280 WorkflowTopologyNode parentWorkflow 281 ) 282 { 283 subordinates.add(newWorkflow); 284 newWorkflow.setParent(parentWorkflow); 285 } 286 287 288 /** 289 * Adds a workflow to the subordinate list of the current workflow. 290 * Before we can add the new workflow, we have to check whether 291 * the new workflow is a parent workflow of any of the current 292 * subordinates (if so, then we have to add the subordinate in the 293 * subordinate list of the new workflow). 294 * 295 * @param newWorkflow the workflow to add in the subordinate list 296 */ 297 private void addSubordinate( 298 WorkflowTopologyNode newWorkflow 299 ) 300 { 301 // Dont try to add the workflow to itself. 302 if (newWorkflow == this) 303 { 304 return; 305 } 306 307 // Check whether subordinates of current workflow should move to the 308 // new workflow subordinate list. 309 ArrayList<WorkflowTopologyNode> curSubordinateList = 310 new ArrayList<WorkflowTopologyNode>(getSubordinates()); 311 312 for (WorkflowTopologyNode curSubordinate: curSubordinateList) 313 { 314 DN newDN = newWorkflow.getBaseDN(); 315 DN subordinateDN = curSubordinate.getBaseDN(); 316 317 // Dont try to add workflow when baseDNs are 318 // the same on both workflows. 319 if (newDN.equals(subordinateDN)) { 320 return; 321 } 322 323 if (subordinateDN.isDescendantOf(newDN)) 324 { 325 removeSubordinate(curSubordinate); 326 newWorkflow.addSubordinate(curSubordinate); 327 } 328 } 329 330 // add the new workflow in the current workflow subordinate list 331 addSubordinateNoCheck(newWorkflow, this); 332 } 333 334 335 /** 336 * Remove a workflow from the subordinate list. 337 * 338 * @param subordinate the subordinate to remove from the subordinate list 339 */ 340 public void removeSubordinate( 341 WorkflowTopologyNode subordinate 342 ) 343 { 344 subordinates.remove(subordinate); 345 } 346 347 348 /** 349 * Tries to insert a new workflow in the subordinate list of one of the 350 * current workflow subordinate, or in the current workflow subordinate list. 351 * 352 * @param newWorkflow the new workflow to insert 353 * 354 * @return <code>true</code> if the new workflow has been inserted 355 * in any subordinate list 356 */ 357 public boolean insertSubordinate( 358 WorkflowTopologyNode newWorkflow 359 ) 360 { 361 // don't try to insert the workflow in itself! 362 if (newWorkflow == this) 363 { 364 return false; 365 } 366 367 // the returned status 368 boolean insertDone = false; 369 370 DN parentBaseDN = getBaseDN(); 371 DN newBaseDN = newWorkflow.getBaseDN(); 372 373 // dont' try to insert workflows when baseDNs are the same on both 374 // workflows 375 if (parentBaseDN.equals(newBaseDN)) 376 { 377 return false; 378 } 379 380 // try to insert the new workflow 381 if (newBaseDN.isDescendantOf(parentBaseDN)) 382 { 383 // the new workflow is a subordinate for this parent DN, let's 384 // insert the new workflow in the list of subordinates 385 for (WorkflowTopologyNode subordinate: getSubordinates()) 386 { 387 insertDone = subordinate.insertSubordinate(newWorkflow); 388 if (insertDone) 389 { 390 // the newBaseDN is handled by a subordinate 391 break; 392 } 393 } 394 395 // if the newBaseDN is not handled by a subordinate then the workflow 396 // is inserted it in the current workflow subordinate list 397 if (! insertDone) 398 { 399 addSubordinate(newWorkflow); 400 insertDone = true; 401 } 402 } 403 404 return insertDone; 405 } 406 407 408 /** 409 * Removes the current workflow from the parent subordinate list 410 * and attach the workflow subordinates to the parent workflow. 411 * 412 * Example: the workflow to remove is w2 413 * 414 * w1 w1 415 * | / \ 416 * w2 ==> w3 w4 417 * / \ 418 * w3 w4 419 * 420 * - Subordinate list of w1 is updated with w3 and w4. 421 * - Parent workflow of w3 and w4 is now w1. 422 */ 423 public void remove() 424 { 425 // First of all, remove the workflow from the parent subordinate list 426 WorkflowTopologyNode parent = getParent(); 427 if (parent != null) 428 { 429 parent.removeSubordinate(this); 430 } 431 432 // Then set the parent of each subordinate and attach the subordinate to 433 // the parent. 434 for (WorkflowTopologyNode subordinate: getSubordinates()) 435 { 436 subordinate.setParent(parent); 437 if (parent != null) 438 { 439 parent.addSubordinateNoCheck(subordinate, parent); 440 } 441 } 442 } 443 444 445 /** 446 * Gets the list of workflow subordinates. 447 * 448 * @return the list of workflow subordinates 449 */ 450 public ArrayList<WorkflowTopologyNode> getSubordinates() 451 { 452 return subordinates; 453 } 454 455 456 /** 457 * Gets the highest workflow in the topology that can handle the requestDN. 458 * The highest workflow is either the current workflow or one of its 459 * subordinates. 460 * 461 * @param requestDN The DN for which we search for a workflow 462 * @return the highest workflow that can handle the requestDN 463 * <code>null</code> if none was found 464 */ 465 public WorkflowTopologyNode getWorkflowCandidate( 466 DN requestDN 467 ) 468 { 469 // the returned workflow 470 WorkflowTopologyNode workflowCandidate = null; 471 472 // does the current workflow handle the request baseDN? 473 DN baseDN = getParentBaseDN(requestDN); 474 if (baseDN == null) 475 { 476 // the current workflow does not handle the requestDN, 477 // let's return null 478 } 479 else 480 { 481 // is there any subordinate that can handle the requestDN? 482 for (WorkflowTopologyNode subordinate: getSubordinates()) 483 { 484 workflowCandidate = subordinate.getWorkflowCandidate(requestDN); 485 if (workflowCandidate != null) 486 { 487 break; 488 } 489 } 490 491 // none of the subordinates can handle the requestDN, so the current 492 // workflow is the best root workflow candidate 493 if (workflowCandidate == null) 494 { 495 workflowCandidate = this; 496 } 497 } 498 499 return workflowCandidate; 500 } 501 502 503 /** 504 * Dumps info from the current workflow for debug purpose. 505 * 506 * @param leftMargin white spaces used to indent the traces 507 * @return a string buffer that contains trace information 508 */ 509 public StringBuilder toString(String leftMargin) 510 { 511 StringBuilder sb = new StringBuilder(); 512 513 // display the baseDN 514 DN baseDN = getBaseDN(); 515 String workflowID = this.getWorkflowImpl().getWorkflowId(); 516 sb.append(leftMargin + "Workflow ID = " + workflowID + "\n"); 517 sb.append(leftMargin + " baseDN:["); 518 if (baseDN.isNullDN()) 519 { 520 sb.append(" \"\""); 521 } 522 else 523 { 524 sb.append(" \"" + baseDN.toString() + "\""); 525 } 526 sb.append(" ]\n"); 527 528 // display the root workflow element 529 sb.append(leftMargin 530 + " Root Workflow Element: " 531 + getWorkflowImpl().getRootWorkflowElement() + "\n"); 532 533 // display parent workflow 534 sb.append(leftMargin + " Parent: " + getParent() + "\n"); 535 536 // dump each subordinate 537 sb.append(leftMargin + " List of subordinates:\n"); 538 ArrayList<WorkflowTopologyNode> subordinates = getSubordinates(); 539 if (subordinates.isEmpty()) 540 { 541 sb.append(leftMargin + " NONE\n"); 542 } 543 else 544 { 545 for (WorkflowTopologyNode subordinate: getSubordinates()) 546 { 547 sb.append(subordinate.toString(leftMargin + " ")); 548 } 549 } 550 551 return sb; 552 } 553 554 }