WorkflowTopologyNode.java revision d25372dc8e65a9ed019a88fdf659ca61313f1b31
/*
* 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;
}
}