At some point you will want to work with data of model instances from your application. When manipulating a model instance, make sure to take into consideration:
An execution context can exist on different execution levels so one context element can have different values in context versions on different levels.
This mechanism serves to separate possibly transient data from the "real" data: the real data exist in the contexts on the base level or 0
level, the execution level where model instances are created. Consequently, for example, changes on shared records in contexts on this level are reflected instantly in the database.
From a custom object, you can create contexts on sublevels: on the sublevel context you work with copies of the contexts and their data. Once happy with the changes, you can merge your changes into its super contexts. Sublevels are designated with an additional digit added to the original level digit separated by a colon, for example 0:1
, 0:1:1
, 0:1:2
etc.
Note: The GUI mechanism provided by the ui module makes use of execution-level mechanism:
- Each form is created on the so-called screen level
- Contexts of View Models are created on sublevels referred to as evaluation levels.
Restrictions
createModelInstance()
and sendSignal()
.To create an execution level, call com.whitestein.lsps.engine.state.xml.EvaluationLevelUtils.nextSublevel(String level, ModelInstance modelInstance)
.
Note that the level does not contain any data when created: the data of non-base levels are loaded when you request an entity that is not present on that level. The system attempts to load it from the immediate parent context and, if not available, it continues to request higher context levels until it reaches the base level. It is when you change the context data that a context on a non-base level stores data.
To merge all changes from a non-base context into the context on the parent level, use the com.whitestein.lsps.engine.lang.EvaluationLevelMerger.mergeLevel(String level)
.
On merge, the system checks for data conflicts. For example, if a variable is changed in two contexts on the second level (the one above the base level) and both context are merged to the base-level context, a conflict is detected. In the case of records, the conflict check is performed on each property: If the property P1 of a record R is changed in one context and property P2 of the same record R is changed in another context no conflict is detected during merge.
To clean changes in a level, call one of the com.whitestein.lsps.engine.state.xml.EvaluationLevelUtils methods:
cleanDataOfEvaluationLevel(ModelInstance modelInstance, String level)
cleanDataOfLevelAndSublevels(ModelInstance, String)
.Note that the cleanDataOfLevelAndSublevels(ModelInstance, String)
method cleans changes in the given level and in all child levels.
removeDataOfEvaluationLevel(ModelInstance, String level)
removeDataFromLevelAndSublevels(ModelInstance, String level)
: removes all entities that belong to the execution level and its sub-levelsTo check if there are changes in the non-base levels, call com.whitestein.lsps.engine.state.xml.ModelInstance.isDirty()
.
To create a record instance in the execution context of your custom object, use the createRecord()
method of the namespace.
//custom form component implementation (the class extends com.whitestein.lsps.vaadin.forms.FormComponent):
RecordHolder colorHolder = getNamespace().createRecord("forms::Color");
//custom task and function:
factory.createRecord(
"GoogleCalendar::GoogleCalendarEvent",
new HashMap<String, Object>() {
{ put("title", event.getTitle().getPlainText());
put("content", event.getTextContent().getContent().getPlainText());
put("date", new Date(event.getTimes().get(0).getStartTime().getValue()));
}
}
);
To work more efficiently with record instances and their types in the LSPS Application, generate the Java wrapper classes for the types and use these classes rather than using the RecordHolder
class to work with Records.
Example of difference in coding
// original access via context:
RecordHolder record = ctx.getNamespace().createRecord("core::ConstraintViolation");
record.setProperty("message", msg);
// better with generated Java class for records:
ConstraintViolationRecord better = new ConstraintViolationRecord(record);
better.setMessage(msg);
When you generate Java wrappers for records, the system creates the following:
To generate Java classes and interfaces for records, do the following:
Right-click the module or project with the records and go to Generate > Record Java Sources and, in the dialog, define the export properties:
The Java sources are generated into a subpage with the name of the module.
Additional record types: other records that should be included in the operation
The parameter is primarily intended for inclusion of library records.
To work with constraints on a record, use the following methods:
executionContext.getProcessModel().getConstraints(recordA, propertyA)
executionContext.getProcessModel().findTag(qid)
A Signal is a special object used for communication within a model instance or with another model instance.
You can produce signals in your custom object using the server API. To catch signals, use the Signal Start Events or Catch Signal Intermediate Events.
Note: You can also use signal modeling elements to work with signals in your models and signal-related standard-library functions, such as
sendSignal()
.
You can work with signals as follows:
//start method of a custom task
@Override
public Result start(TaskContext context) throws ErrorException {
Decimal d = (Decimal) context.getParameter("number");
Integer i = d.intValueExact();
//adding the signal to the task type context:
context.addSignal(
"prime check output:"
+ System.out.println(org.apache.commons.math3.primes.Primes.isPrime(i))
);
return Result.FINISHED;
}
Send a signal with the sendSync()
or sendAsynch()
method of the CommunicationService bean.
Example sendSync() call from the start method of a custom task type
@EJB
private CommunicationService communicationService;
...
@Override
public Result start(TaskContext context) throws ErrorException {
long modelInstanceId = 71000;
String signal = "signal";
SignalMessage signalMessage = new SignalMessage(
//sender of the signal:
Identifier.ofModelInstance(context.getModelInstance().getId()),
//receiver of the signal:
Identifier.ofModelInstance(71000), new ObjectValue(signal));
try {
//sending the signal:
communicationService.sendSync(signalMessage);
} catch (ModelInstanceNotFoundException | InvalidModelInstanceStateException e) {
e.printStackTrace();
}
return Result.FINISHED;
}
You can throw errors directly from your custom objects.
To catch error, use the Error Start Events or Error Intermediate Events.
To throw an error, do the following:
error(<error_code>)
function. com.whitestein.lsps.common.ErrorException
. To perform an action always when an instance of a model is created, started, or finished, do the following:
public class MyModelInstancePlugin implements ModelInstancePlugin {
@Override
public void onCreate(CreateCommand cmd, ModelInstanceEntity modelInstance) {
System.out.println(">>>>>>>>>>>>>>>>>> This is on create hook <<<<<<<<<<<<<<<<");
}
...
@EJB(beanName = "MyModelInstancePlugin")
private ModelInstancePlugin myMIPlugin;
@Override
protected void registerCustomComponents() {
register(myMIPlugin, ModelInstancePlugin.class);
}
To use the command-line tool from your application, do the following:
mconsolecl
dependency in your pom.xml: <dependency>
<groupId>com.whitestein.lsps.mconsolecl</groupId>
<artifactId>lsps-mconsole-cl</artifactId>
<version>${project.version}</version>
</dependency>
com.whitestein.lsps.mconsolecl.Main.main(new String[]{"arg 1", "arg 2"});
For details about the console are available in the Management documentation.