Fork me on GitHub

Service Layer

Process configurable action

Services

Overview

A Service Object is a JRebirth Component (part of CSM pattern, Command-Service-Model).
It can be retrieved from the ServiceFacade.

A Service can hold several Tasks defined by a WaveType. Each Task requires:

  1. Define Call Wave Type (entry point)
  2. Define Return Wave Type (exit point)
  3. Register the Callback
  4. Define the Task process into the right method name.

That's all ! You don't have to bother about threading issues and asynchronous tasks.

Warning:
You must pay attention to the lifecycle of your service instance. As a JRebirth Component, each service object is eligible to garbage collection if it isn't retained by another object currently used.
So your data stored or processed into your service can be loss if you didn't manage correctly your Service life.
The most easy way is to hold your service with a strong reference into a long-living object like a top-levelModel.

Short UML Diagram:

Service Class Diagram

Defining Wave Types

Entry Point Wave Type

This is the WaveType used to process a Wave generated anywhere into the application.

41
42
43
44
45
/** Wave type use to load events. */
WaveType DO_LOAD_EVENTS = Builders.waveType("LOAD_EVENTS")
                                  .items(EditorWaves.EVENTS_FILE)
                                  .returnAction("EVENTS_LOADED")
                                  .returnItem(EditorWaves.EVENTS);

This WaveType uses only one WaveItem to store the file that must be loaded. WaveItem wrap the type of the object we want to use, thus it's possible to check that API contract isn't broken.

67
68
69
/** The file containing all events serialized. */
WaveItemBase<File> EVENTS_FILE = new WaveItemBase<File>() {
};

Exit Point Wave Type

The return Wave Type is automatically created with by the using the returnAction value with returnItem as WaveItem. The code above will generate a WaveType that uses only one WaveItem to store the list of events loaded from the given file.

71
72
73
/** The name of the events. */
WaveItemBase<List<JRebirthEvent>> EVENTS = new WaveItemBase<List<JRebirthEvent>>() {
};

Task Registration

Each Task requires to be registered in order to generate the right WaveType that wrap the return value.
This registration must be done into the JRebirth's Component void initService() method like this:

54
55
56
57
58
@Override
public void initService() {
 
    listen(DO_LOAD_EVENTS);
}

If you don't declare the return WaveType a exception will be thrown at runtime when trying to send back the Service Task output.

If your Service Task doesn't return anything (void return) you can dismiss this step except if you want to receive an empty notification when the Job is done; just use JRebirthItems.voidItem.

Perform the Job!

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
public void initService() {
 
    listen(DO_LOAD_EVENTS);
}
 
/**
 * {@inheritDoc}
 */
@Override
@Priority(RunnablePriority.High)
public List<JRebirthEvent> doLoadEvents(final File selecteFile, final Wave wave) {
 
    final List<JRebirthEvent> eventList = new ArrayList<>();
 
    updateMessage(wave, "Parsing events");
 
    // Get number of line to calculate the task progression
    final int totalLines = ServiceUtility.countFileLines(selecteFile);
 
    try (BufferedReader br = new BufferedReader(new FileReader(selecteFile));)
    {
        int processedLines = 0;
 
        String strLine = br.readLine();
 
        // Read File Line By Line
        while (strLine != null) {
            processedLines++;
 
            updateProgress(wave, processedLines, totalLines);
 
            if (strLine.contains(JRebirthMarkers.JREVENT.getName())) {
                // Convert the string to a JRebirth event and add it to the list
                addEvent(eventList, strLine.substring(strLine.indexOf(">>") + 2));
            }
 
            // Read the next line to process
            strLine = br.readLine();
        }
 
    } catch (final IOException e) {
        LOGGER.error("Error while processing event file", e);
    }
    return eventList;
 
}

How to use the Service Feature

Call the Service

To call this Service Feature, you can use the returnData from any JRebirth's Component. It takes at least 2 mandatory arguments:

  1. The Service Class Object
  2. The WaveType that is related to the Service Feature
  3. An unordered list of WaveData objects that wrap values required by WaveType contract

Hereafter you will fin an example of service call with only one arguments passed:

58
59
60
returnData(LoadEdtFileService.class,
           LoadEdtFileService.DO_LOAD_EVENTS,
           Builders.waveData(EditorWaves.EVENTS_FILE, selected));

Process the Service Result

The Service Feature Result is sent as a Wave that wrap returned value. So to be informed when the result is available, there is two things to do :

  • Let your component listening this WaveType.
  • Add Wave handler code to process the result.

Each JRebirth's Component are able to listen some WaveType's waves by calling the listen method with one or several Wave Types.
This call must be done into one of the following method according to component used:

  • initCommand : for Command classes
  • initService : for Service classes
  • initModel : for Model classes

Each initXXX method is called into JRebirth Internal Thread by ready method.

38
39
40
41
42
@Override
protected void initModel() {
    listen(LoadEdtFileService.DO_LOAD_EVENTS.returnWaveType());
    listen(EditorWaves.DO_UNLOAD);
}

Add a method that suit the WaveType convention.
The name must used the predefined prefix (in our case DO_ converted to do), then the WaveType's name converted in a camel cased format.
Method parameters must be compliant with Wave Items defined into the Wave Type.
A final parameter must be added, the Wave itself taht could be useful to get extra data, for example when chained waves are used.

51
52
53
public void doEventsLoaded(final List<JRebirthEvent> eventList, final Wave wave) {
    getView().activateButtons(!eventList.isEmpty());
}

Threading

Which Threads are involved ?

All communications with Service Component is done into JIT and all Service Tasks are performed into one slot of JTP or HPTP according to priority defined with annotation.

Threading Priority

Each Service feature call will be processed by the JTP (JRebirth Thread Pool) or by the HPTP (High Priority Thread Pool) according to their predefined priority.
By default the Priority is set to RunnablePriority.Low (a level below Normal) to let other task to be performed into JTP before Service feature calls (like Command).

Its possible to increase or decrease this value by adding an annotation on the Service Feature method like this:

55
56
@Priority(RunnablePriority.High)
List<JRebirthEvent> doLoadEvents(final File selecteFile, final Wave wave);

Follow Task progression

Each Service Task are able to update a progress bar with integer value and message. In both cases you must provide the wave provided as a method argument, it will be used to link the associated ServiceTask and find the right ProgressBar and Text widgets to update.
You can update the message test by calling this method:

You can update progress bar indicator by calling this method: