Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

OTPattern/PerThreadRole

Intent

A team should adapt a certain base behaviour in a per-thread manner, where team state may differ for different control flows.

Motivation

Many teams intercept more than one join point in the base application and more often than not the team should preserve some state between interceptions.

In order to detect which interceptions should be seen as part of the same behavioural context object identity is not always sufficient, because different join points may be triggered at different base objects which are not always linked in a way that supports easy detection of object groups.

If the connection between different interceptions is actually best described by a control flow dependency the team should provide access to a reference object that represents the current control flow thus supporting to easily maintain per-thread state.

Structure

The team selects one role as the representative for a control flow. This is the role that observes the join point anchoring the observed control flow. The team provides a per-thread reference to an instance of this role class.

Participants

This pattern involves

  • One ContextTeams spanning the overall context
  • One ContextRole representing the individual per-thread contexts
  • Any number of SecondaryRoles which may or may not be sub-divided into ObserverRoles and ActuatorRoles.

Collaboration

The ContextTeam holds the per thread reference like this

public team class ContextTeam {
        ThreadLocal<ContextRole> context = new ThreadLocal<ContextRole>();
        protected class ContextRole ...
}

The ContextRole intercepts the anchoring join point and registers itself at the ContextTeam for the duration of this join point:

...
        protected class ContextRole playedBy Base1 {
                // state variables:
                protected SomeType someState;
                // (un)registration:
                register <- replace anchorJoinpoint;
                callin void register() {
                        ContextTeam.this.context.set(this);
                        try {
                                base.register();
                        } finally {
                                ContextTeam.this.context.set(null);
                                ContextTeam.this.unregisterRole(this, ContextRole.class);
                        }
                }
        }

SecondaryRoles can now

  • use availability of a ContextRole in their guards to restrict their activation to the given control flow
  • access state variables of ContextRole (write and read) for passing information from one join point to the other
...
        protected class ObserverRole playedBy Base2
                base when (ContextTeam.this.context.get() != null)
        {
                observer <- after observationPoint;
                void observer() {
                        ContextTeam.this.context.get().someState = ... // some state computation
                }
        }
        protected class ActuatorRole playedBy Base3
                base when (ContextTeam.this.context.get() != null)
        {
                actuate <- after triggerPoint
                        when (someConditionUsing(ContextTeam.this.context.get()));
 
                void actuate() {
                        ContextRole context = ContextTeam.this.context.get();
                        if (someOtherConditionUsing(context))
                                someActionUsing(context);
                }
        }

Note, how this pattern seemlessly supports the ObserversMediatorActuators architectural pattern.

Implementation

The above implementation makes access to the enclosing team explicit using ContextTeam.this.context throughout. Of course the abbreviated use of context is possible, too.

If a guard accesses state of the ContextRole this is doubly secured:

  • the top-level guard checkes this reference for null
  • if the top-level guard were omitted, any NullPointerException within the guard would simply cause the guard to evaluate to false (see OTJLD §5.4.c).

Known Uses

This pattern was first observed in the org.objectteams.otdt.pde.ui plugin in team class org.objectteams.otdt.pde.validation.BundleValidation (source in SVN). Here role BundleCheckingContext is the per-thread context role.

Related Patterns

This pattern is well suited as a supporting pattern for the ObserversMediatorActuators archtectural pattern.

For some situations an even simpler solution can be achieved using AssociateByNesting.

Back to the top