Next column


UML 2 Activity and Action Models

Part 3: Control Nodes

 

Conrad Bock, U.S. National Institute of Standards and Technology


space COLUMN

PDF Icon
PDF Version

This is the third in a series introducing the activity model in the Unified Modeling Language, version 2 (UML 2), and how it integrates with the action model [1]. The previous article addressed the execution characteristics of actions in general, and additional functionality of actions that invoke behaviors [2]. The first article gave an overview of activities and actions that is assumed here [3]. The remainder of the series elaborates other specific elements. This article covers control nodes, which route control and data through the flow model. It also points out the differences in concurrency support between UML 2 and UML 1.x activities.


1 CONTROL NODES

To recap, UML 2 activities contain nodes connected by edges to form a complete flow graph. Control and data values flow along the edges and are operated on by the nodes, routed to other nodes, or stored temporarily. More specifically, action nodes operate on control and data they receive via edges of the graph, and provide control and data to other actions; control nodes route control and data through the graph; and object nodes hold data temporarily as they wait to move through the graph. Data and object are unified in UML under the notion of classifier, so they are used interchangeably. The term "token" is shorthand for control and data values that flow through an activity.

Figure 1: Control Nodes

There are seven kinds of control node, with five notations, as shown in Figure 1. Contrary to the name, control nodes route both control and data/object flow. Each of them is described in the sections below.

2 INITIAL NODES

Flow in an activity starts at initial nodes. They receive control when an activity is started and pass it immediately along their outgoing edges. No other behavior is associated with initial nodes in UML. Initial nodes cannot have edges coming into them. For example, in Figure 2, when the Deliver Mail activity is started, a control token is placed on the initial node, notated as a filled circle, and immediately flows along to start the Get Mail action.

Figure 2: Initial Node

An activity can contain more than one initial node. A single control token is placed in each one when the activity is started, initiating multiple flows. It might be clearer to use one initial node connected to a fork node to initiate multiple flows simultaneously (see section 5), but this is up to the modeler. Other ways to start flows in an activity will be discussed later in the series.

If an initial node has more than one outgoing edge, only one of the edges will receive control, because initial nodes cannot copy tokens as forks can (see section 5). In principle, the edges coming out of initial nodes can have guards and the semantics will be identical to a decision node (see next section). For convenience, initial nodes are excepted from the general rule that control nodes cannot hold tokens waiting to move downstream, if it happens that all the guards fail. In general, it is clearer to use explicit decision points and object nodes than to depend on these fine points of initial nodes.

3 DECISION NODES

Decision nodes guide flow in one direction or another, but exactly which direction is determined at runtime by edges coming out of the node. Usually edges from decision nodes have guards, which are Boolean value specifications evaluated at runtime to determine if control and data can pass along the edge. The guards are evaluated for each individual control and data token arriving at the decision node to determine exactly one edge the token will traverse. For example, Figure 3 shows a decision node, notated as a diamond, choosing between flows depending on whether an order can be filled or not. Value specifications in UML 2 are often just strings interpreted in an implementation-dependent way1. In this example, the modeler's intention for the strings "accepted" and "rejected" must already be understood by the implementation or defined by additional modeling. Model refinement can introduce an additional explicit behavior, such as a decision input behavior, explained below.

Figure 3: Decision Node

The order in which the above guards are evaluated is not constrained by UML, and can even be evaluated concurrently. For this reason, guards should not have side effects, to prevent implementation-dependent interactions between them. If guards are to be evaluated in order, as is typical in conditional programming constructs, then decision nodes can be chained together, one for each guard, combined with the predefined guard "else". The else guard can be used with decision nodes for a single outgoing edge to indicate that it should be traversed if all the other guards from the decision node fail. Figure 4 shows an example of chained decision nodes with else guards. The CLOSE ORDER action is reached by failure of the non-else guards2.

Figure 4: Chained Decision Nodes

Since guard evaluation order is implementation-dependent, the modeler should also arrange that only one guard succeed, otherwise there will be race conditions among them. It is up to the implementation whether to finish evaluating guards after one is found that succeeds. In theory, all the guards might succeed at one time, in which case the semantics is not defined3. If all the guards fail, then the failing control or data token remains at the object node it originally came from, since control nodes cannot hold tokens waiting to move downstream, as object nodes can. Token queuing is discussed later in the series.

If the guards involve a repeated calculation of the same value, a behavior on the decision node can determine this value once for each token arriving at the decision node, and then provide it to the outgoing guards for testing. For example, Figure 5 shows a decision input behavior Is Order Acceptable providing a Boolean result tested by the outgoing guards4 (the curved arrow is not part of UML notation, see earlier articles on inputs and outputs of actions and activities). Each order arriving at the decision node is passed to Is Order Acceptable before guards are evaluated on the outgoing edges5. The output of the behavior is available to the guards, in an implementation-dependent way, as with all value specifications (see footnote 1). The value specifications in Figure 5 happen to use the name of the output parameter of the decision behavior.

Figure 5: Decision Input Behavior

A repository model for part of Figure 5 is shown in Figure 6 (see first article for more about the UML repository [3]). The two anonymous object flows are separate repository elements for the two object flows coming out of the decision node. Each has an opaque expression as a guard, which are the kind of value specification that are completely implementation-interpreted. Each object flow targets its own separate anonymous input pin, each of which provide input to their respective behaviors, one for each direction of flow from the decision6.

Figure 6: Repository for Part of Figure 5

Other factors besides guards can determine whether control and data can pass along an edge, and consequently which edge will be traversed out nodes, including decision nodes. Future articles will address edges and token queuing in more detail. Whatever factors are involved, the purpose of a decision node is to ensure that each control and data token arriving at the decision node traverses no more than one of the outgoing edges.

4 MERGE NODES

Merge nodes bring together multiple flows. All control and data arriving at a merge node are immediately passed to the edge coming out of the merge. No other behavior is associated with merge nodes in UML7. Merge nodes have the same notation as decision nodes, but merges have multiple edges coming in and one going out, whereas it is the opposite for decision nodes. Flows coming into a merge are usually alternatives from an upstream decision node. For example, Figure 7 shows a merge node bringing two flows together to close an order. The merge is required, because if the two flows went directly into CLOSE ORDER, both flows would need to arrive before closing the order, which would never happen [2]8. Merge can be used with concurrent flows also, see Figure 16 in section 6.

Figure 7: Merge Node with Alternate Flows

Flows from chained decision nodes can be merged more flexibly than with conditional constructs in structured programming languages. For example, Figure 8 shows two of three flows from a decision node being merged separately from the third. Flows coming out of a decision node do not need to be brought together by a merge at all. See Figure 20 in section 7.

Figure 8: Merge Nodes without Nesting

Figure 9 on the left shows a shorthand notation for a merge immediately followed by a decision. It has the same effect as the separate merge and decision shown on the right. Both have the same repository model, which contains separate merge and decision nodes.

Figure 9: Merge/Decision Combination

5 FORK NODES

Fork nodes split flows into multiple concurrent flows. Control and data arriving at a fork are duplicated across the outgoing edges. No other behavior is associated with fork nodes in UML. For example, in Figure 10 control or data tokens leaving Receive Order are copied by the fork, notated as a line segment, and passed to Fill Order and Send Invoice simultaneously. Since object tokens are only references to objects, copying them does not duplicate the objects themselves, only the references to them. There is no synchronization of the behaviors on concurrent flows in UML 2 activities, as there are in UML 1.x activities, which are a kind of state machines. In Figure 10, the flow to Ship Order can complete long before Send Invoice is even finished, or vice versa9, 10.

 

Figure 10: Fork Node

The default semantics for flows coming out of an action is that they are all initiated when the action completes. This creates concurrent flows, but data outputs from actions are not copied. The action outputs a separate value for each flow. Action outputs are also placed on pins, which are a kind of object node, and consequently hold values as they wait to move downstream. See the second article for more information on action outputs [2]. In UML 1.x, data flows are based on state transitions, so only one flow is initiated when the state (action) is exited [8]. See section 6 for analogous points about action inputs.

6 JOIN NODES

Join nodes synchronize multiple flows. In the common case, control or data must be available on every incoming edge in order to be passed to the outgoing edge. Join nodes have the same notation as fork nodes, but joins have multiple edges coming in and one going out, whereas it is the opposite for fork nodes. Flows coming into a join are usually concurrent flows from an upstream fork. For example, Figure 11 shows a join node synchronizing two flows to Close Order. Both Ship Order and Add Account Payable must complete before Close Order can start.

Figure 11: Join Node

Join nodes take one token from each of the incoming edges and combine them according to these rules:

  1. If all the incoming tokens are control, then these are combined into a single control token for the outgoing edge.
  2. If some of the incoming tokens are control and others are data, then these are combined to provide only the data tokens to the outgoing edge. The control tokens are destroyed.

For example, in Figure 11 the join combines control tokens from Ship Order and Add Account Payable into one, so that Close Order is executed once instead of twice11, 12.

The effect is the same if the join is omitted and the two flows go directly into Close Order, because the action would wait for both of them anyway. It might be clearer to use the join, especially since UML 1.x activities would have needed only one flow to arrive, as with all state machines [8]. However, if the flows were carrying data, two tokens will be passed along the outgoing edge after synchronization, and go to a single pin in Close Order. This would have the undesirable effect of Close Order executing twice13, and would not even be executable if the data is of incompatible types, because they would both be directed at the same input pin (see earlier articles for explanation of pins). For example, Ship Order might output a tracking record, and Add Account Payable the new account payable, both of which are needed as input to Close Order. In this case, the data flows should be directed to two pins on Close Order, without the join, as shown in Figure 12. This is another example of model refinement. Figure 11 might be taken as a process sketch and refined later into Figure 12, when it is clear what inputs are needed to close an order.

Figure 12: Joining Data Flows with Pins

Modelers should ensure that joins do not depend on control or data flows that may never arrive. For example, in Figure 13 when the problem report is not a high priority, the top flow is directed to a flow final (see next section), so control will never reach the join. This is corrected in Figure 14. See equivalent diagram in Figure 1714.

Figure 13: Join Node Anti-pattern

Figure 14: Join Node Pattern

It is not required that flows coming out of a fork be synchronized. For example, Figure 15 shows only some of the flows from a fork going to a join. The order is closed after it is shipped and invoiced, but the account payable might be monitored for a long period after that, so is not synchronized with closing the order. Concurrent flows can also be merged rather than joined, as shown in Figure 16. In this example, part inspection is serialized, while two parts can be made in parallel. The Inspect Part action will be executed twice, once for each part arriving on concurrent flows15. This requires more than one token moving on the same flow line at one time. Multi-token flows are discussed later in the series. These are more examples of the expressiveness introduced in UML 2 activities over UML 1.x activities.

Figure 15: Fork with Partial Join

Figure 16: Fork with Merge

Modelers can specify the conditions under which a join accepts incoming control and data using a join specification, which is a Boolean value specification associated with join nodes. The default inherited from UML is "and", with the semantics described so far. Other join specifications can be given, using the name of the incoming edges to refer to the control or data arriving at the join. For example, Figure 17 shows an alternative to Figure 14 that substitutes a join specification for the merge node. The edges are named with single letters in this example, but can be any string.

Figure 17: Join Specification

Figure 18 on the left shows a shorthand notation for a join immediately followed by a fork. It has the same effect as a separate join and fork, as shown on the right. Both have the same repository model, which contains separate join and fork nodes.

Figure 18: Join/Fork Combination

7 FINAL NODES

Flow in an activity ends at final nodes. The most innocuous form is the flow final, which takes any control or data that comes into it and does nothing. Flow final nodes cannot have outgoing edges so there is no downstream effect of tokens going into a flow final, which are simply destroyed. Since object tokens are just references to objects, destroying an object token does not destroy the object. Figure 19 extends Figure 10 with flow finals at the end. Each flow could have its own flow final and the effect would be the same. Activities terminate when all tokens in the graph are destroyed, so this one will terminate when both flows reach the flow final.

Figure 19: Flow Final Node

Activity final nodes are like flow final nodes, except that control or data arriving at them immediately terminates the entire activity. This makes a difference if more than one control or data token might be flowing in the graph at the time the activity final is reached, as in Figure 19. An activity final cannot be used instead of a flow final there because the completion of one concurrent flow would terminate the other16. In Figure 20 on the other hand, it does not matter whether a flow final or activity final is used, the execution traces are the same. Also each flow could have its own activity final on the end and the effect would be the same.

Figure 20: Activity Final Node

Figure 21 is an example where the termination functionality of activity finals is used in an intentional race between flows. This is a process for buying movie tickets by having people stand in separate lines until one gets the tickets for the group. The fastest line will produce a token to the activity final and terminate the other flow.

Figure 21: Activity Final Node, Racing Example

8 CONCLUSION

This is the third in a series on the UML 2 activity and action models. This article focuses on control nodes, which route control and data through an activity. The execution semantics of each kind of control node is described, along with the differences in concurrency from UML 1.x activities. UML 2 activities do not have the restrictions on concurrent flow that UML 1.x activities inherited from state machines. In particular, UML 2 concurrent flows are fully distributed in execution, not synchronized action-by-action as UML 1.x activities are. UML 2 forks and joins can be more flexibly paired with each other and other control nodes, rather than one-for-one as in UML 1.x activities. UML 2 action outputs and inputs also have concurrency and synchronization semantics, whereas they did not in UML 1.x.

ACKNOWLEDGEMENTS

Thanks to Evan Wallace and James Odell for their input to this article.


Footnotes

1 UML 1.x more forthrightly called these "uninterpreted strings", but UML 2 value specifications can also be instance specifications, and opaque or structured. Activities use value specifications in some places and behaviors in others. Whether this is done by any consistent rationale will be addressed in finalization.

2 Conditional constructs can also be modeled with a ConditionalNode. This is one of the aspects of activities for modeling programming language constructs. These will be covered later in the series.

3 See discussion of undefined semantics in section 6 of the second article [2].

4 An alternate notation is { decisionInput = Is Order Acceptable } placed near the decision node.

5 Object flow edges are usually distinguished from control edges by rectangles representing the type of object that is flowing, for example as pins on actions in Figure 5. It is a presentation option in UML to omit these rectangles as in Figure 3, for example if they are obvious to the reader or confusing to subject matter experts, while still storing the model for them in an underlying UML repository. Special views such as this are a way activities support a wide range of the development cycle, from process sketching to executable program specifications. Model refinement is another technique, which refers to multiple models for the same process existing over time, linked in a progression as detail is added. For example, a subject matter expert might draw a diagram like Figure 3 without pins, and a more UML-knowledgeable modeler might add them later. A record of refinements can be kept using the upcoming Query, View, and Transformation technology [4]. This is a simple example of the general problem of recording design evolution, to ensure that the end product fulfills the original requirements, as in systems engineering for manufacturing [5].

6 The current UML specification implies that decision input behaviors only apply to data tokens, but does not explicitly restrict them to that. This is to be clarified in finalization. A decision input behavior for control flow can in principle have no parameters and return a value based on other data, such as available from the host object of the entire activity. The host object is retrieved with the action ReadSelfAction. In general, if a behavior requires information that cannot be retrieved from values provided by its input parameters, it can use ReadSelfAction. This action is discussed later in the series.

7 Use join nodes for more complex semantics, see section 6.

8 UML 1.x activities would require only one of the transitions to arrive to start the action, as do all state machines. With UML 2 the example in the UML User Guide, Figure 19-9, is correct, whereas it was incorrect in UML 1.x [6][7].

9 Concurrent or orthogonal regions in state machines are synchronized through the run-to-completion semantics, which requires that behaviors invoked by the state machine complete before a new event is pulled from the input queue. This forces actions in concurrent regions to proceed in lockstep with each other. The “do” activity on states allows events to be processed while the activity is executing, but it also allows events to interrupt the do activity, which is not usually the desired effect in flow modeling.

10 The current UML specification requires control and data tokens to either traverse all outgoing edges from a fork or none of them. This means if the outgoing edges have guards or other characteristics that prevent tokens from moving, that none of the concurrent flows will be initiated. The intention is for outgoing edges to start concurrent flows that are not otherwise prevented. This will be addressed in finalization.

11 This requires one of the control tokens to be held somewhere while the other flow arrives, which is not technically possible, since control is output without pins. This will be addressed in finalization.

12 It would be useful to have the option to combine object tokens for identical objects, especially in cases that two tokens are duplicate because they were copied by an upstream join.

13 This actually depends on the multiplicity of the input parameter. If the input parameter multiplicity on CLOSE ORDER has a lower bound of two, it will consume both tokens coming from the join in one execution of the action. Multi-token flows are discussed later in the series.

14 This is a good situation to use edge connectors, which are a notational technique for shortening the length of activity edge arrows, by breaking them up into a beginning and ending segment. See Figure 211 of the UML 2 specification [1].

15 It is also not required for flows coming into a join to be concurrent. For example, if a loop upstream generates alternate flows to a join, the synchronized flows will occur at completely different times.

16 This can be resolved by inserting a join after Ship Order and Add Account Payable that leads to an activity final. Then the activity would only terminate after both flows are done.

 

REFERENCES

[1] Object Management Group, “UML 2.0 Superstructure Specification,” http://www.omg.org/cgi-bin/doc?ptc/03-08-02, August 2003.

[2] Bock, C., “UML 2 Activity and Action Models, Part 2: Actions”, in Journal of Object Technology, vol. 2, no. 5, September-October 2003, pp. 41-56. http://www.jot.fm/issues/issue_2003_09/column4

[3] Bock, C., "UML 2 Activity and Action Models," in Journal of Object Technology, vol. 2, no. 4, July-August 2003, pp. 43-53. http://www.jot.fm/issues/issue_2003_07/column3

[4] Object Management Group, “MOF 2.0 Query/Views/Transformations RFP,” http://www.omg.org/cgi-bin/doc?ad/02-04-10, April 2002.

[5] Shooter, S.B., Keirouz, W.T., Szykman, S., Fenves, S. J., "A Model for the Flow of Design Information in Product Development," Journal of Engineering with Computers, vol. 16, 2000, pp. 178-194.

[6] Booch, G., Rumbaugh, J., and Jacobson, I., The Unified Modeling Language User Guide, Addison-Wesley, 1999.

[7] Bock, C., “Unified Behavior Models,” Journal of Object-Oriented Programming, vol. 12, no. 5, September 1999.

[8] Object Management Group, “OMG Unified Modeling Language, version 1.5,” http://www.omg.org/cgi-bin/doc?formal/03-03-01, March 2003.

 

About the author


space Conrad Bock is a Computer Scientist at the National Institute of Standards and Technology, specializing in process models. He is one of the authors of UML 2 activities and actions, and can be reached at conrad.bock at nist.gov.


Cite this column as follows: Conrad Bock: “UML 2 Activity and Action Models Part 3: Control Nodes”, in Journal of Object Technology, vol. 2, no. 6, pp. 7-23. http://www.jot.fm/issues/issue_2003_11/column1


 

Next column