Tuesday, October 17, 2006

Away but I'm back!

Still alive! I've been working on a model review for one of my clients, but I should have some time this week to push the action meta forward and catch up on the discussion groups.

- Leon

Thursday, October 05, 2006

Progress on the AL metamodel

The notes posted here have been a great help in getting the model started. I've made a lot of progress in the last few days. For now I am focusing on specification (non-runtime) information. Three specification subsystems are emerging - Action Blocks, Actions and Flow. Action Blocks is very small - it just captures the relationship among Operations, States and Services with Action Blocks and Parameters. Actions specializes what I've been calling "processes" into subclassifications. The action subclasses are then related to metamodel components like Class, Attribute, Event etc. For example, the Transmitter Action must specify an Event. By the way, I'm renaming "Transmitter" to Broadcaster to match the UML action semantics vocabulary. I keep referring to the UML terminology as much as possible. Finally, the Flow subsystem captures the graph nature of interconnected Actions. It consists of classes like Flow Type, Node Type, Pin, Process, Store and so forth.

Naturally, I will post my models once I have a reviewable baseline.

Sunday, October 01, 2006

Linking and Unlinking

Processes (continued)


You may have noticed that relationship linking was not mentioned in the last batch of notes. It's not like I forgot them - alright I admit it - I forgot them. But I'm kind of glad I did as a bit of thought was required to sort out this seemingly trivial activity. And even then, I'm not sure I've got the best plan - but it helps to get things down on virtual paper. Remember that nothing in this blog is set in stone - just explorations to fuel the modeling activity. So feel free to register your comments if you think I'm going astray. Going astray is one of my specialties.

Linker Processes

A linker process creates or modifies links in a relationship. The link activity described in Mellor/Balcer's Executable UML (p. 120) is a primitive accessor that will be implemented as a core behavior in the metamodel. The primitive link activity takes two object references and a relationship name. (Though a perspective must also be specified to create a link on a binary reflexive association). That, notwithstanding, there is still work to be done before we can supply two distinct object references to our primitive linker.

Assume that we route two object flows into a linker process. During run-time, any number of objects may be streaming along a given flow. Some object flows may come up empty and some may contain too many object references. How do we match up multiple object references from each flow? One approach would be to distinguish "single" object flows and stores from "many" object flows and stores. This is what is done in many executable UML action languages. Wouldn't it be cool though, if we could shift the burden of the one-many selection/store distinction away from the modeler to the underlying execution model? The downside is that we must imbue the action execution engine with the smarts to multitudinous disparity in a predictable way. In that spirit, here we go. These are the link activities processes that must be supported:

  • association linker
  • association unlinker
  • generalized object creator (normal object creator redefined)
  • generalized object eraser (normal object creator redefined)
  • reclassifier

We start using the / symbol to separate a role or informal name from a defined element. So if we see something like 1 / object_flow, we know that the name or phrase left of the /, "1" in this case, is just an informal name whereas the element on the right has a definition somewhere in the action language notes. In other words, there is an object_flow named "1".

Association Linker

Links one object to another (or the same object) on an association.

Flows In: { status } 1 / object_flow < 2 / object flow >
Flows Out: < association class / object_flow >
Specification: rnum

Let's explore each variation.

Binary non-reflexive
( Dog Owner - 1 - is owned by -- owns - 1..* - Dog )

The modeler connects two object flows to the linker. The active perspective will be automatically connected to flow 1 and the other to flow 2. For example, one object pool might be labeled lazy_dogs and the other new_owner. Each generates a seperate object flow into the linker.

Binary reflexive
( Aircraft - 0..1 - takes off after -- takes off before - 0..1 - Aircraft )

The modeler specifies which direction to link by routing the active perspective to flow 1 and the passive perspective to flow 2. This cannot be determined automatically, so it is up the modeler to set these as intended. For example, assuming that "takes off before" is the active perspective (arbitrarily in this case), the flow for aircraft_entering_queue would go to flow 1 and the last_in_queue flow would be 2.

Unary (symmetric reflexive) association
( Territory - 1..* -- borders )

Since the perspective is symmetric it doesn't matter which flow is 1 or 2. If, for example, we link Territory Brazil to Venezuela on "borders" it's means exactly the same as if we did the reverse.

Cyclic link
To link an object to itself on a binary reflexive or unary association only one input flow, object flow 1, is required. It is the same result as flowing two different reference to the same object on the 1 and 2 inputs.

Perspective Multiplicity and Flow Cardinality

Consider the 1, 2 object flow inputs into the association linker. Either object flow could be empty, contain a single object reference or stream multiple object references during run-time. Lets refer to the empty, single and multiple quantities of object references as the cardinality of the flow. How should the linker connect the objects from each stream given each possible mix of cardinality?

The empty case is easily squared away. If at least one input is empty then no links can be made since we always link objects referred to in flow 1 with those in flow 2. The only exception is the cyclic link case mentioned earlier. In this case input 2 is not used and no link is made if input 1 is empty.

This leaves us with these flow cardinality combinations for inputs 1,2: (1, 1), (1, 2..*), (2..*, 1), and (2..*, 2..*). Before we can determine how to link the objects referenced in the paired flows, we need to know the multiplicity of the corresponding perspective of each flow.
First, a quick aside regarding association perspectives. In * UML, the "binary" in binary association does not refer to the class on each side. That is because, in the case of a reflexive binary association, there is only one class, e.g. Plane takes off after Plane. Instead "binary" refers to the two perspectives on the association, e.g. takes off before / takes off after. In the metamodel we needed a way to distinguish between each perspective. We could have used a generic label such as the A and B perspective but that's not very descriptive. Most of the time (in English, at least) we can distinguish the active and passive voice. For the perspectives "owns" and "is owned by", for example, we recognize "owns" as the active side. This rule doesn't always work. The perspectives "is before / is after" are both active. But this is a less frequent situation and we are no worse off than the A/B labeling solution.
Now if we follow the convention of running the object flow corresponding to the active perspective into flow input 1 on the linker with the passiver perspective object flow going into input 2, it is easier to describe how the objects will be linked. In the table below we cross all flow combinations with all perspective multiplicities. Conditionality (0..1, 0..*) is removed from consideration so we are just looking at pure multiplicity in the leftmost column. So S means single (0..1 or 1) and M means many (* or 1..*).




Flow Cardinality (1, 2)



1, 1 1, 2..* 2..*, 1 2..*, 2..*
Mult (a,p) S, S

one->one one->one one->one { one->one }

S, M
one->one one->all first->one first->all

M, M
one->one one->all all->one all X all

The plan is to link as many objects together as possible given the supply of object references. The normal (intended use) cases are highlighted in green. Possibly useful in, uh maroon? and shaded for those of doubtful utility. Let's look at the normal cases first.

The association multiplicity and flow cardinality matches in each of the normal cases.

Multiplicity (S, S) - Flow (1, 1)
A pair of object references arrives, one on each flow. The object referenced in flow 1 is linked to the object referenced in flow 2.

Multiplicity (S, M) - Flow (1, 2..*)
The single multiplicity corresponds to the single object flow and the many multiplicity corresponds to the multiple object flow. The single object referenced in flow 1 is linked to all objects referenced in flow 2.

Multiplicity (M, M) - Flow (2..*, 2..*)
Each object references supplied in one flow is linked to each object reference from the other flow to create a cross product. So active(1, 2), passive (3, 4, 5) would yield links 1-3, 1-4, 1-5, 2-3, 2-4, 2-5

In the next case, some object references may be ignored.

Multiplicity (S, M) - Flow (2..*, 2..*)
Each object referenced in flow 1 is linked to the next object in flow 2. This pairing continues until there are no more objects in flow 1. Any remaining objects referenced in flow 2 are dropped.

In the remaining cases, some object references will be ignored.

Multiplicity (S, M) or (M, M) - Flow (1, 1)
Since only two object references are delivered, there is only one linking choice. The two referenced objects are linked together.

Multiplicity (S, S) - Flow (1, 2..*) or (2..*, 1)
The single object referenced on either flow can only be linked once. Consequently, only one link can be made with all remaining object references dropped.

Multiplicity (S, M) - Flow (2..*, 1) or (2..*, 2..*)
Since only one object referenced in flow 1 can be linked, the first object referenced in flow 1 is linked to the first or only object referenced in flow 2. All other object references are dropped.

Multiplicity (M, M) - Flow (1, 2..*) or (2..*, 1)
The object referenced from the flow with cardinality 1 is linked to all objects referenced in the flow with cardinality 2..*.

Association Unlinker

An association unlinker breaks existing links on an association relationship. If there is an association class present, each corresponding association object will be removed automatically.

Flows In: { status } 1 / object_flow < 2 / object flow >
Flows Out: None
Specification: rnum

Unlinking can be defined with or without targets specified. Use of object flows 1 and 2 vary depending on the type of association.

Binary non-reflexive
( Dog Owner - 1 - is owned by -- owns - 1..* - Dog )

Target specified

We can unlink one or more objects in one class from one or more specific objects in another class. Each set of object references is input in a separate flow. Object references form the active perspective will automatically be assigned to flow 1. For example, one object pool might be labeled lazy_dogs and the other new_owner. Each link that exists between a member of flow 1 to a member of flow 2 will be erased.

Target not specified

If we use only one object flow, all links to the referenced objects in that flow will be erased. Let's say we flow from an object pool named dead_dogs into the unlinker. Each link to an object referenced in dead_dogs will be broken.
A quick aside about unconditional constraints. We can see from the multplicity on the example association that a Dog Owner must own at least one Dog. In each of our unlink examples one or more dog owners could have been left dogless. To maintain data integrity, the modeler will need to specify some activity to either delete the owner objects or assign new dogs. Now, we may find a way to make this burden easier as we flesh out the action language. But that's going to require some serious analysis. So, for now, assume that no magic maintains unconditional integrity unless otherwise specified.
Binary reflexive
( Router - 0..* - sends mail to -- gets mail from - 0..* - Router )

Target specified

The modeler specifies which direction to unlink by routing the active perspective to flow 1 and the passive perspective to flow 2. This cannot be determined automatically, so it is up the modeler to set these as intended. Let's say that we have a Router object C that both sends and receives mail from a Router object D. The active perspective should be "sends mail to". To unlink in the send direction, from C to D, the object flow with the reference to D ("sends data to" - D) must be 1 (active) with C on flow 2 (passive). If unlinking in both directions was desired, the "not specified" method is probably a better choice.

Target not specified

Only one input flow is supplied in this case. For example, to unlink any object sending or receiving from C, output a reference to C on one object flow into the unlinker. All links involving C on the specified association will be erased.

Unary (symmetric reflexive) association
( Territory - 1..* -- borders )

Target specified

Two object flows are required and assignment to 1 and 2 is arbitrary. Each object referenced in one flow is unlinked with any connected object referenced in the other flow.

Target not specified

Only one object flow is required. All links are broken to each object referenced in the flow.

Cyclic unlink (target specified is itself)
To unlink an object from itself on a binary reflexive or unary association only object flow 1, is required. It is the same result as flowing two different reference to the same object on the 1 and 2 inputs.

Generalized Object Creator

Our definition of creator from the previous post must be extended to accommodate the peculiarities of generalized/specialized objects. In a * UML generalization, a single object is represented by two instances, one in the superclass and one in a subclass. The creator must create both of these instances. The name of the subclass must be specified.

If an object participates in more than one generalization, all participating instances must be inserted by into the metamodel by the creator. Consider the following arrangement of generalizations:

A <R1 [ B | C ], C <R2 [ D | E ], F <R3 [ E | B ]

Here we have three generalizations R1, R2 and R3 with superclasses A, C and F respectively. To create an object in this example model, a non-specialized (leaf) subclass must be specified. If we specify E, for example, instances will be created in classes E, F, C and A. For subclasses, our object creator both creates and links. Any initialization activity coordinated amongst the instances must be modeled explicitly. Only the instance creation/linking is automatic.

Multidirectional generalization requires the specification of one subclass per direction:

G <R4 [ H | I ], G <R5 [ J | K ]

The set of possible creation specifications are all combinations of non-specialized subclasses linked to a common superclass. So in the model example above we have: (H, K), (H, J), (I, J), (I, K).

One attribute flow per created super or subclass instance may be provided. (This means that the definition of an attribute flow in the previous post must be amended to include a class_name in the flow content. And since I can just do that now, I will. Done.)

Specification: 1{ non-specialized_subclass_name }
Flows In: { status } { attribute }
Flows Out: < object >

The output flow returns a reference to each specified subclass. References to the newly created superclass instances can be found using with a selector process traversing the generalization(s).

Generalized Object Eraser

We will now extend our eraser process so that it can handle generalizations properly. An object in a generalization, super or subclass, is represented by two instances. If an object participates in multiple generalizations there will be one more instance per additional generalization. Every one of these related instances will be eliminated by the object eraser.

Referring back to the generalized object creator example models, let's say that we specify a reference to an instance of class F. Following instances must be erased: F, either E or B, A and if E then also C

The modeler must be careful to not specify deletion until all cleanup and coordination for every instance involved has been accomplished.

Flows In: { status } object

Reclassifier

A reclassifer migrates a subclass instance in a generalization to another subclass at the same level. This means that the original subclass instance must be erased and a new instance created in the destination subclass. Both subclasses must be at the same level in the same generalization. Based on our earlier model examples in the creator and eraser, you can probably guess that the activity doesn't necessarily stop there!

Let's say that we want to migrate C to B. We must delete and unlink the D or E subclass under C (but keep the D/E superclass F intact. Then we delete the C instance, create a B instance and link the B instance to both the A and F superclasses. Any supplied attribute data can be used to initialized the values in B (B only since that is the only created instance).

Once again, it is the modelers responsibility to ensure that any coordination and cleanup activity in the subclasses to be deleted is completed before triggering the reclassification.

Specification: from_subclass_name, to_subclass_name
Flows In: { status } { attribute }
Flows Out: < object >

The output flow returns a reference to the newly created subclass instance.

Conclusion


Okay, that's all for now. I'm going to need some spicy indian food and bad television before getting back to the metamodel.

- Leon