/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2007-2008 Sun Microsystems, Inc. */ package org.opends.server.core; import java.util.ArrayList; import org.opends.server.types.*; import org.opends.server.workflowelement.WorkflowElement; /** * This class implements a workflow node. A workflow node is used * to build a tree of workflows (aka workflow topology). Each node * may have a parent node and/or subordinate nodes. A node with no * parent is a naming context. * * Each node in the workflow topology is linked to a WorkflowImpl * which contains the real processing. The base DN of the workflow * node is the base DN of the related WorkflowImpl. * * How the workflow topology is built? * A workflow node is a subordinate of another workflow node when * the base DN of the former workflow is an ancestor of the base DN * of the latter workflow. * * A subtree search on a workflow node is performed on the node itself as * well as on all the subordinate nodes. */ public class WorkflowTopologyNode extends WorkflowTopology { // Parent node of the current workflow node. private WorkflowTopologyNode parent = null; // The list of subordinate nodes of the current workflow node. private ArrayList<WorkflowTopologyNode> subordinates = new ArrayList<WorkflowTopologyNode>(); /** * Creates a new node for a workflow topology. The new node is initialized * with a WorkflowImpl which contains the real processing. Optionally, * the node may have tasks to be executed before and/or after the real * processing. In the current implementation, such pre and post workflow * elements are not used. * * @param workflowImpl the real processing attached to the node * @param preWorkflowElements the list of tasks to be executed before * the real processing * @param postWorkflowElements the list of tasks to be executed after * the real processing */ public WorkflowTopologyNode( WorkflowImpl workflowImpl, WorkflowElement<?>[] preWorkflowElements, WorkflowElement<?>[] postWorkflowElements ) { super(workflowImpl); } /** * Executes an operation on a set of data being identified by the * workflow node base DN. * * @param operation the operation to execute * * @throws CanceledOperationException if this operation should * be canceled. */ public void execute(Operation operation) throws CanceledOperationException { // Execute the operation getWorkflowImpl().execute(operation); // For subtree search operation we need to go through the subordinate // nodes. if (operation.getOperationType() == OperationType.SEARCH) { executeSearchOnSubordinates((SearchOperation) operation); } } /** * Executes a search operation on the subordinate workflows. * * @param searchOp the search operation to execute * * @throws CanceledOperationException if this operation should * be canceled. */ private void executeSearchOnSubordinates(SearchOperation searchOp) throws CanceledOperationException { // If the scope of the search is 'base' then it's useless to search // in the subordinate workflows. SearchScope originalScope = searchOp.getScope(); if (originalScope == SearchScope.BASE_OBJECT) { return; } // Elaborate the new search scope before executing the search operation // in the subordinate workflows. SearchScope newScope = elaborateScopeForSearchInSubordinates(originalScope); searchOp.setScope(newScope); // Let's search in the subordinate workflows. WorkflowResultCode workflowResultCode = new WorkflowResultCode( searchOp.getResultCode(), searchOp.getErrorMessage()); DN originalBaseDN = searchOp.getBaseDN(); for (WorkflowTopologyNode subordinate: getSubordinates()) { // We have to change the operation request base DN to match the // subordinate workflow base DN. Otherwise the workflow will // return a no such entry result code as the operation request // base DN is a superior of the subordinate workflow base DN. DN subordinateDN = subordinate.getBaseDN(); // If the new search scope is 'base' and the search base DN does not // map the subordinate workflow then skip the subordinate workflow. if ((newScope == SearchScope.BASE_OBJECT) && !subordinateDN.getParent().equals(originalBaseDN)) { continue; } // If the request base DN is not a subordinate of the subordinate // workflow base DN then don't search in the subordinate workflow. if (! originalBaseDN.isAncestorOf(subordinateDN)) { continue; } // Set the new request base DN and do execute the // operation in the subordinate workflow. searchOp.setBaseDN(subordinateDN); subordinate.execute(searchOp); boolean sendReferenceEntry = workflowResultCode.elaborateGlobalResultCode( searchOp.getResultCode(), searchOp.getErrorMessage()); if (sendReferenceEntry) { // TODO jdemendi - turn a referral result code into a reference entry // and send the reference entry to the client application } } // Now we are done with the operation, let's restore the original // base DN and search scope in the operation. searchOp.setBaseDN(originalBaseDN); searchOp.setScope(originalScope); // Update the operation result code and error message searchOp.setResultCode(workflowResultCode.resultCode()); searchOp.setErrorMessage(workflowResultCode.errorMessage()); } /** * Sets the parent workflow. * * @param parent the parent workflow of the current workflow */ public void setParent(WorkflowTopologyNode parent) { this.parent = parent; } /** * Gets the parent workflow. * * @return the parent workflow. */ public WorkflowTopologyNode getParent() { return parent; } /** * Indicates whether the root workflow element is encapsulating a private * local backend or not. * * @return <code>true</code> if the root workflow element encapsulates * a private local backend */ public boolean isPrivate() { return getWorkflowImpl().isPrivate(); } /** * Gets the base DN of the workflow that handles a given dn. The elected * workflow may be the current workflow or one of its subordinate workflows. * * @param dn the DN for which we are looking a parent DN * @return the base DN which is the parent of the <code>dn</code>, * <code>null</code> if no parent DN was found */ public DN getParentBaseDN(DN dn) { if (dn == null) { return null; } // parent base DN to return DN parentBaseDN = null; // Is the dn a subordinate of the current base DN? DN curBaseDN = getBaseDN(); if (curBaseDN != null) { if (dn.isDescendantOf(curBaseDN)) { // The dn may be handled by the current workflow. // Now we have to check whether the dn is handled by // a subordinate. for (WorkflowTopologyNode subordinate: getSubordinates()) { parentBaseDN = subordinate.getParentBaseDN(dn); if (parentBaseDN != null) { // the dn is handled by a subordinate break; } } // If the dn is not handled by any subordinate, then it is // handled by the current workflow. if (parentBaseDN == null) { parentBaseDN = curBaseDN; } } } return parentBaseDN; } /** * Adds a workflow to the list of workflow subordinates without * additional control. * * @param newWorkflow the workflow to add to the subordinate list * @param parentWorkflow the parent workflow of the new workflow */ private void addSubordinateNoCheck( WorkflowTopologyNode newWorkflow, WorkflowTopologyNode parentWorkflow ) { subordinates.add(newWorkflow); newWorkflow.setParent(parentWorkflow); } /** * Adds a workflow to the subordinate list of the current workflow. * Before we can add the new workflow, we have to check whether * the new workflow is a parent workflow of any of the current * subordinates (if so, then we have to add the subordinate in the * subordinate list of the new workflow). * * @param newWorkflow the workflow to add in the subordinate list */ private void addSubordinate( WorkflowTopologyNode newWorkflow ) { // Don't try to add the workflow to itself. if (newWorkflow == this) { return; } // Check whether subordinates of current workflow should move to the // new workflow subordinate list. ArrayList<WorkflowTopologyNode> curSubordinateList = new ArrayList<WorkflowTopologyNode>(getSubordinates()); for (WorkflowTopologyNode curSubordinate: curSubordinateList) { DN newDN = newWorkflow.getBaseDN(); DN subordinateDN = curSubordinate.getBaseDN(); // Don't try to add workflow when baseDNs are // the same on both workflows. if (newDN.equals(subordinateDN)) { return; } if (subordinateDN.isDescendantOf(newDN)) { removeSubordinate(curSubordinate); newWorkflow.addSubordinate(curSubordinate); } } // add the new workflow in the current workflow subordinate list addSubordinateNoCheck(newWorkflow, this); } /** * Remove a workflow from the subordinate list. * * @param subordinate the subordinate to remove from the subordinate list */ public void removeSubordinate( WorkflowTopologyNode subordinate ) { subordinates.remove(subordinate); } /** * Tries to insert a new workflow in the subordinate list of one of the * current workflow subordinate, or in the current workflow subordinate list. * * @param newWorkflow the new workflow to insert * * @return <code>true</code> if the new workflow has been inserted * in any subordinate list */ public boolean insertSubordinate( WorkflowTopologyNode newWorkflow ) { // don't try to insert the workflow in itself! if (newWorkflow == this) { return false; } // the returned status boolean insertDone = false; DN parentBaseDN = getBaseDN(); DN newBaseDN = newWorkflow.getBaseDN(); // don't try to insert workflows when baseDNs are the same on both // workflows if (parentBaseDN.equals(newBaseDN)) { return false; } // try to insert the new workflow if (newBaseDN.isDescendantOf(parentBaseDN)) { // the new workflow is a subordinate for this parent DN, let's // insert the new workflow in the list of subordinates for (WorkflowTopologyNode subordinate: getSubordinates()) { insertDone = subordinate.insertSubordinate(newWorkflow); if (insertDone) { // the newBaseDN is handled by a subordinate break; } } // if the newBaseDN is not handled by a subordinate then the workflow // is inserted it in the current workflow subordinate list if (! insertDone) { addSubordinate(newWorkflow); insertDone = true; } } return insertDone; } /** * Removes the current workflow from the parent subordinate list * and attach the workflow subordinates to the parent workflow. * * Example: the workflow to remove is w2 * * w1 w1 * | / \ * w2 ==> w3 w4 * / \ * w3 w4 * * - Subordinate list of w1 is updated with w3 and w4. * - Parent workflow of w3 and w4 is now w1. */ public void remove() { // First of all, remove the workflow from the parent subordinate list WorkflowTopologyNode parent = getParent(); if (parent != null) { parent.removeSubordinate(this); } // Then set the parent of each subordinate and attach the subordinate to // the parent. for (WorkflowTopologyNode subordinate: getSubordinates()) { subordinate.setParent(parent); if (parent != null) { parent.addSubordinateNoCheck(subordinate, parent); } } } /** * Gets the list of workflow subordinates. * * @return the list of workflow subordinates */ public ArrayList<WorkflowTopologyNode> getSubordinates() { return subordinates; } /** * Gets the highest workflow in the topology that can handle the requestDN. * The highest workflow is either the current workflow or one of its * subordinates. * * @param requestDN The DN for which we search for a workflow * @return the highest workflow that can handle the requestDN * <code>null</code> if none was found */ public WorkflowTopologyNode getWorkflowCandidate( DN requestDN ) { // the returned workflow WorkflowTopologyNode workflowCandidate = null; // does the current workflow handle the request baseDN? DN baseDN = getParentBaseDN(requestDN); if (baseDN == null) { // the current workflow does not handle the requestDN, // let's return null } else { // is there any subordinate that can handle the requestDN? for (WorkflowTopologyNode subordinate: getSubordinates()) { workflowCandidate = subordinate.getWorkflowCandidate(requestDN); if (workflowCandidate != null) { break; } } // none of the subordinates can handle the requestDN, so the current // workflow is the best root workflow candidate if (workflowCandidate == null) { workflowCandidate = this; } } return workflowCandidate; } /** * Dumps info from the current workflow for debug purpose. * * @param leftMargin white spaces used to indent the traces * @return a string buffer that contains trace information */ public StringBuilder toString(String leftMargin) { StringBuilder sb = new StringBuilder(); // display the baseDN DN baseDN = getBaseDN(); String workflowID = this.getWorkflowImpl().getWorkflowId(); sb.append(leftMargin + "Workflow ID = " + workflowID + "\n"); sb.append(leftMargin + " baseDN:["); if (baseDN.isNullDN()) { sb.append(" \"\""); } else { sb.append(" \"" + baseDN.toString() + "\""); } sb.append(" ]\n"); // display the root workflow element sb.append(leftMargin + " Root Workflow Element: " + getWorkflowImpl().getRootWorkflowElement() + "\n"); // display parent workflow sb.append(leftMargin + " Parent: " + getParent() + "\n"); // dump each subordinate sb.append(leftMargin + " List of subordinates:\n"); ArrayList<WorkflowTopologyNode> subordinates = getSubordinates(); if (subordinates.isEmpty()) { sb.append(leftMargin + " NONE\n"); } else { for (WorkflowTopologyNode subordinate: getSubordinates()) { sb.append(subordinate.toString(leftMargin + " ")); } } return sb; } }