Fork me on GitHub

Command Area

Reuse common code and don't be scared by threading issue

Command Overview

A command is an atomic reusable action that could be triggered from anywhere. There are two kinds of Commands:

Command - Class Diagram

Single Command

Single Commands are atomic and are run independently. If you trigger several commands in-a-row you will trigger them in parallel according to their predefined running thread. JRebirth engine will serialize their instantiation and their startup but they will be processed into JAT, JIT or one of JTP slots. JAT and JIT will process command one after the other. JTP will act in the same manner but internal engine will dispatch all actions to its pooled threads (by default {2 x Number of CPU core} slots are available by Thread Pool).

MultiCommand

MultiCommand provides the ability to run some Single Commands sequentially (even across several threads) or in parallel.

Hereafter you will find an example of MultiCommand used to display a model UI:

34
35
36
37
38
39
40
41
42
43
44
45
46
public class ShowModelCommand extends DefaultMultiBeanCommand<DisplayModelWaveBean> {
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected List<UniqueKey<? extends Command>> defineSubCommand() {
        return Arrays.asList(
                             getCommandKey(PrepareModelCommand.class),
                             getCommandKey(AttachModelCommand.class));
    }
  
}

The multi command code will be run into JIT, but its sub-command will be run respectively into JTP and JAT (according to their own configuration).

Why are they using these threads ?

ShowModelCommand use the annotation defined into DefaultMultiCommand to run into JIT.

PrepareModelCommand use the annotation defined into DefaultPoolCommand to run into JTP.

ShowModelCommand use the annotation defined into DefaultUICommand to run into JAT (it’s mandatory to update scene’s nodes within JAT).

How to trigger a Command

Commands are designed to be disposable after usage, but they could be retained by strong references to be executed twice or more or store data. Each call will return a new instance of the command class, because each command was stored with a timestamp key-based. (Timestamp is added to the default key and also for custom key).

You can create a command using four different ways:

Please note that Commands are JRebirth top-level components (with Services and Models), they follow the component lifecycle as described in Facade Page.

Thus AbstractBaseCommand extends BehavioredComponent and Component and their descendants must provide initCommand(), processAction() and execute() methods.

It’s possible to call a command from any JRebirth component (Command, Service, Model).

You just need to call the getCommand method to build an instance. You can provide either the Command Class or its unique key (if required).

47
58
70
81
<C extends Command> C getCommand(final Class<C> clazz, final Object... keyPart);
<C extends Command> C getCommand(final UniqueKey<C> commandKey);
<C extends Command> List<C> getCommands(final Class<C> clazz, final Object... keyPart);
<C extends Command> List<C> getCommands(final UniqueKey<C> commandKey);

Once you have retrieved your command, you can store it with a strong reference to avoid GC collecting it. Your command life will depend on the lifetime of your strong reference. Thus you will be able to configure directly your command properties. Finally to trigger it you must call its run() method. The perform method of the command will be executed into the Thread Type chosen (JAT, JIT, JTP) by the Command declaration.

36
Wave run();

Be careful: As explained here each call to getCommand could retrieve the same OR another instance of the command class depending on key parts provided and instance’s strong references. (in this case no timestamp is added to the command key)

You can trigger a command execution by calling callCommand from any component, it will use JIT to trigger the command then the command will be run using the thread declared.

You can provide some parameters into WaveData that will be hold by the wave and so available into the command.

240
248
public final <WB extends WaveBean> Wave callCommand(final Class<? extends CommandBean<WB>> commandClass, final WB waveBean) {
public final Wave callCommand(final Class<? extends Command> commandClass, final WaveData<?>... data) {

You can also provide a WaveBean object to store all data.

UI’s controllers provide a convenient method named void linkCommand(Node, EventType<E>, Class<? extends Command>, WaveData<?>…)

This method is useful to declare with only one line of code the call of a command triggered when the chosen JavaFX event occurred on a node belonging to the View.

It’s also possible to add a callback function, in example to manage double-click detection (see LinkedCallback).

54
linkCommand(view().getOpenButton(), MouseEvent.MOUSE_CLICKED, OpenEventTrackerFileCommand.class, LinkedCallback.CHECK_MOUSE_SINGLE_CLICK);

Any Command is a JRebirth component and can benefits of Observer features. It’s possible to listen a WaveType (registration done into the initModel, initService and initCommand methods, or using @OnWave) in order to be notified when a such wave is sent. You can manage custom methods called by reflection to handle waves and can you catch all waves into the processAction(Wave) method.

More information is available in Notifier & Component page.

Be careful, commands can handle asynchronous wave only if they haven’t been collected by the Garbage Collector! So you need to create a instance and to keep a strong references on it somewhere.

Command Properties

A command is an atomic action reusable or not that can be run into a predefined thread. A command provides specific features:

Each command will be launch by JRebirth Internal engine and run into a dedicated thread. Threads involved in a JRebirth application are explained into the Thread page.

The runner thread can be configured using two ways:

The priority rule is : Annotation > Constructor argument > Default value. The default value is : JIT (JRebirth Internal Thread). The top-level annotation will be systematically used overriding lower ones and also constructor arguments.

To run a command into the JAT (JavaFX Application Thread), use this annotation :

31
@RunInto(RunType.JAT)

To run a command into the JIT (JRebirth Internal Thread), use this annotation :

31
@RunInto(RunType.JIT)

To run a command into JTP (JRebirth Thread Pool, the command will be run into a slot), use this annotation :

31
@RunInto(RunType.JTP)

To run a command into the JAT (JavaFX Application Thread), extends the DefaultUICommand class :

33
public class AttachModelCommand extends DefaultUIBeanCommand<DisplayModelWaveBean> {

To run a command into the JIT (JRebirth Internal Thread), extends the DefaultCommand class :

35
public class ChainWaveCommand extends DefaultCommand implements WaveListener {

(implementation of WaveListener is optional)

To run a command into JTP (JRebirth Thread Pool, the command will be run into a slot), extends the DefaultPoolCommand class :

34
public class PrepareModelCommand extends DefaultPoolBeanCommand<DisplayModelWaveBean> {

It’s also possible to define the runType (The thread which will handle the command) by passing RunType, each descendant classes of AbstractBaseCommand offer at least one constructor allowing this enum value to the command constructor.

98
public AbstractBaseCommand(final RunType runType, final PriorityLevel priority) {

The component key as described in the Facade page allow storing unique commands.

All objects provided as key part will be serialized (toString call) to build the key. Warning : When a command is built consequently to a wave reception, the wave UID will be concatenated to the class name instead of using key parts.

Command are run in a custom thread in 3 steps:

  • beforePerform
  • perform
  • afterPerform

Only the perform method must be implemented to execute your action.

A WaveBean is a Java Bean that allow carrying a lot of named properties into a Wave.

Command Classes can declare a generic type that allow to cast the Wave Bean into the right one, it allows to use getWaveBean().getMyProperty() into source code which is more convenient than parsing WaveData (but it implies to create a dedicated Java Class).

Each command is a simple Java Object, you can add fields or JavaFX Properties to help configuring your execution code. You must pay attention that these values will be kept until the command is disposed (after execution if no strong references exists).

For example you can attach a command to a Model and launch it several times while updating command’s properties.