The quote mentions not just rejected but rejected for traversal over the edge. This corresponds to the case that a token is not allowed to go through an edge, and not to the case where a target may not accept it.
In which case is it rejected for traversal ?
Unfortunately, unlike the terms offered and accepted which are well defined, the term rejected is not explicitly defined and need to be interpreted.
To my knowledge it can happen only in the case of a guard, as described in 15.2.3.3
:
An ActivityEdge may have a guard, which is a ValueSpecification that
is evaluated for each token offered to the edge. An offer shall only
pass along an ActivityEdge if the guard for the edge evaluates to true
for the offered token.
In which case is it not rejected for traversal ?
For weight, there is no rejected traversal. The tokens are accumulated in the source until the threshold is reached. In case a guard blocks a token, it's not the traversal that fails but the offer:
If the guard fails for any of the tokens, and this reduces the number
of tokens that can be offered to the target to less than the weight,
then all the tokens fail to be offered.
For selection there's also no rejection of a traversal, but a delay on acceptance (15.2.3.4
) :
selection Behavior is used while offering tokens to the target node,
it may be run many times on the same token before the token is
accepted by the target node.
Additional remarks on 15.3.3.4 and resolution of your scenario
The following statement tells us that in case of a rejected traversal, the token is definitively withdrawn:
If tokens are rejected for traversal, they shall no longer be offered
to the outgoing edge.
The following statement tells us that unless a token is rejected for traversal, it will wait until it is accepted by the target (the acceptance doesn't need to be immediate):
They shall be accepted by the target or rejected for traversal over
the edge
Consequence: In your scenario, b
will wait for a
and they will both together be consumed to generate an output token of the right join node.
And finally, this sentence explains that in case of successful traversal, the tokens are sent sequentially, one by one:
before any more tokens are offered to the outgoing edge
Consequence: In your scenario, as long as b
will wait for an a
, no new b
will be offered by the left join.
Edit: Isn't there an issue with the UML spec here ?
The "one by one" rule doesn't seem fully consistent IMHO:
- The rule is written about the offering part on the output, not the consumption part of the input. This makes the behavior in case of a weight on the output extremely ambiguous: I'd avoid it.
- The rule seems to be written with control tokens in mind. It is not clear to me what happens if 2 different object tokens are on the input (or 2 replicated objects without the CombinedDuplicates): I'd expect all of them to pass the join. But what hapens if one of it gets rejected: would the other continue its way alone ?
Similarly, IMHO the rejection on traversal logic seems not to cover all the cases. For example, what if the output edge goes to a decision node (so it is not rejected), but none of the subsequent guarded branch can accept it ? Shall we understand that the token is rejected (implicitly the decision input would be guarded by the combination of all possible output), or shall we consider it's consumed and the token is waiting in the decision node ?
So I think there is still some work to do for the standard committee in this area.