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 }