Basic use for reading and manipulating ForSyDe models
This quick start focuses on the Java supporting library. Currently, this is the main supporting library of ForSyDe IO.
Topics covered in this tutorial:
- Reading ForSyDe IO Models from disk to memory.
- Saving ForSyDe IO Models from memory to disk.
- Querying and manipulating them in memory for any analysis purposes.
Requirements
Regardless if you are checking this repository from a Linux or Windows OS, you need at least Java 11 to run the I-modules and E-modules.
On Linux you can use the distrubition’s package manager, e.g. apt-get
or dnf
, to install the JVM system wide.
On Windows you can likely find official installer on all major JVM distributions like Oracle.
We recommend using a versioning installer like jabba and installing the lastest stable JVM possible. The modules were tested with Amazon Coretto and the Graal VM.
You also need Gradle installed as well.
(Optional) Downloading SDK, Gradle, and Jabba
This documentation provides step-by-step instructions to download and install SDK, Gradle, and Jabba for your project on Linux and Windows platforms.
Linux
This documentation provides step-by-step instructions to download and install SDK, Gradle, and Jabba for your project.
Prerequisites
- Ensure that you have internet connectivity.
- Make sure that you have a terminal or command prompt available on your system.
Step 1: Downloading SDKMan
- Visit the official SDKMan website at https://sdkman.io/.
- Follow the provided instructions in the official documentation to download and install SDKMan.
Alternatively, you can use the following steps:
- Open your terminal or command prompt.
- Execute the following command to download and install SDKMan:
curl -s "https://get.sdkman.io" | bash
- After installation, execute the following command to configure SDKMan:
source "$HOME/.sdkman/bin/sdkman-init.sh"
(Optional) Check the installed SDKMan version by executing the following command:
sdk version
- Install Gradle by executing the following command:
sdk install gradle 8.1.1
Step 2: Downloading Jabba
- Visit the official Jabba repository on GitHub at https://github.com/shyiko/jabba.
- Follow the provided instructions in the official documentation to download and install Jabba.
Alternatively, you can use the following steps:
- Open your terminal or command prompt.
- Execute the following command to download and install Jabba:
curl -sL https://github.com/shyiko/jabba/raw/master/install.sh | bash && . ~/.jabba/jabba.sh
- Install your preferred version of OpenJDK using Jabba. For example:
jabba install openjdk@1.17.0
Windows
This documentation provides step-by-step instructions to download and install SDK, Gradle, and Jabba on a Windows machine for your project.
Prerequisites
- Ensure that you have internet connectivity.
- Make sure that you have a command prompt available on your Windows machine.
Step 1: Downloading SDKMan
- Visit the official SDKMan website at https://sdkman.io/.
- Follow the provided instructions in the official documentation to download and install SDKMan.
Alternatively, you can use the following steps:
- Open a command prompt.
- Execute the following command to download and install SDKMan:
powershell -nop -c "iex ((new-object net.webclient).DownloadString('https://get.sdkman.io'))"
- After installation, execute the following command to configure SDKMan:
source "$HOME/.sdkman/bin/sdkman-init.sh"
(Optional) Check the installed SDKMan version by executing the following command:
sdk version
- Install Gradle by executing the following command:
sdk install gradle 8.1.1
Step 2: Downloading Jabba
- Visit the official Jabba repository on GitHub at https://github.com/shyiko/jabba.
- Follow the provided instructions in the official documentation to download and install Jabba.
Alternatively, you can use the following steps:
- Open a command prompt.
- Execute the following command to download and install Jabba:
@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://github.com/shyiko/jabba/raw/master/install.ps1'))"
- Install your preferred version of OpenJDK using Jabba. For example:
jabba install openjdk@1.17.0
Congratulations! You have successfully downloaded and installed SDKMan, Gradle, and Jabba for your project.
Setting up your tool project
After navigating to a folder you wish to start your tool or script, and issue
gradle init
which ask you what type of project you wish to create and then create a handful of files in the directory that we will work with.
Ideally you should choose application
as the most basic one, in case you do not fully know how to work with Maven/Gradle based JVM projects.
Among the questions asked, you can choose the default for most, except for the minimum java version, which should be 17.
Don’t worry if this number seems high to you, it is the latest LTS after 11, and it will be supported for many years; it is just
that Java moves slowly but surely, specially its using companies.
You should have now a directory like
│ .gitattributes
│ .gitignore
│ gradlew
│ gradlew.bat
│ settings.gradle
│
├───.gradle
│ file-system.probe
│
├───app
│ │ build.gradle
│ │
│ └───src
│ ├───main
│ │ ├───java
│ │ │ └───temp
│ │ │ App.java
│ │ │
│ │ └───resources
│ └───test
│ ├───java
│ │ └───temp
│ │ AppTest.java
│ │
│ └───resources
└───gradle
└───wrapper
gradle-wrapper.jar
gradle-wrapper.properties
The most import file for now is build.gradle
, which declaritely defines the project’s dependencies and also fetches them.
To enable and fetch ForSyDe IO in your new project, you need to first enable JitPack as a repository:
repositories {
maven { url 'https://jitpack.io' }
}
And then add to the modules to the dependencies
section of your build.gradle
:
dependencies {
implementation "com.github.forsyde.forsyde-io:forsyde-io-java-core:0.7.10" // or newer
implementation "com.github.forsyde.forsyde-io:forsyde-io-java-libforsyde:0.7.10" // or newer
}
The first added dependency is actually independent of ForSyDe (as in, the umbrella project).
It provides the meta-modelling abstractions of system graphs and trait hierarchies as describe in this academic paper,
including classes, exporters, importers among others.
The second dependency adds the “ForSyDe Lib” trait hierarchy to the ClassPath so that it can be used for making sense of the ForSyDe umbrella project traits.
For example, it is within “ForSyDe Lib” that the trait SDFActor
exists; otherwise, you can still load the model successfully, but SDFActor
will only be an opaque trait.
Just for this tutorial, you can also download the small example toy_sdf_tiny.fiodl
from the forsyde-io/examples/sdf
folder (in the repo).
We will read this serialized model in memory and print its contents just to see it works.
Now, on your main
method inside the App
class (or equivalent in your bigger project), you can do the following:
// add this imports....
import forsyde.io.core.ModelHandler;
import forsyde.io.lib.hierarchy.ForSyDeHierarchy;
// within the main class, interface etc
public static void main(String[] args) {
var handler = new ModelHandler();
handler.registerTraitHierarchy(new ForSyDeHierarchy());
var model = handler.loadModel("path/to/toy_sdf_tiny.fiodl");
System.out.println(model.toString());
}
This likely won’t compile because loading models can throw exceptions, so you can add a throws
exception to the main signature.
If this worked fine, you should get a big one line that is simply the string representation of the system graph in question.
SystemGraph(...includes p1 and p2...)
You could naturally also write the model out again for fun and see how the “standard” identation of a fiodl
file is:
// same imports...
public static void main(String[] args) {
var handler = new ModelHandler();
handler.registerTraitHierarchy(new ForSyDeHierarchy());
var model = handler.loadModel("path/to/toy_sdf_tiny.fiodl");
handler.writeModel(model, "itworksnice.fiodl");
}
From which you should get a itworksnice.fiodl
file in your executing folder.
Besides from managing to put a ForSyDe IO model in memory and later write it back to disk, this tutorial
show a key element of the tooling process: ModelHandler
.
The ModelHandler
is essentially the assigned API for reading and writing models from and to disk,
it automates things like model-to-model transformations, validations (if they exist) and transforming traits
from opaque to known entities. This is the reason you can find many registerX
where X
is a variety of things
in the ModelHandler
. For example, if we would add the java-core-bridge-sdf3
dependency to this project,
then the SDF3Driver
becomes available, and we can spice up the model handler via,
handler.registerDriver(new SDF3Driver());
which now enables the loadModel
function to load SDF3 files, i.e. .sdf.xml
files!
We shall not cover it in this tutorial, but enabling bridges to other MDE framework boils down to developing drivers
and adapters that can be registered in the ModelHandler
, which then takes care of it automatically in any code that
is already using it.
If you have a fiodl
file on disk that was created with supporting libraries prior to 0.7+, be sure
to register the migrator that comes with libforsyde
so that all the tools consuming them are updated
in a best-effort basis:
handler.registerSystemGraphMigrator(new TraitNamesFrom0_6To0_7())
Programmatic creation, manipulation and querying
The previous example is nice to show how things can be used from a very broad perspective, but we are interested
in inspecting the elements a bit more closely and creating scripts that can process these model elements.
Here, there are two key elements for us to known and remember: SystemGraph
and ForSyDeHierarchy
.
SystemGraph
is basically what some would call the “backbone” or the “database” of the entire operation.
You can create just like any normal class:
var m = new SystemGraph();
from where you can create vertexes and edges contained in this graph,
var v1 = m.newVertex("v1");
var v2 = m.newVertex("IPreferLongIds");
m.connect(v1, v2);
which creates two trait-less vertexex in m
and connects them with a trait-less edge.
What we just did is good for educational purposes but useless from any system analysis perspective.
These two vertexes are simply two data components lying in the model without any indication of their purposes.
Now we will use the ForSyDeHierarchy
to change that, by using layers of useful information within the vertexes and edges.
Let’s say we want to create a Synchronous process in the system graph m
that is neither v1
or v2
(and their respective IDs).
We could do the following:
var newProc = ForSyDeHierarchy.SYComb.enforce(m, m.newVertex("syproc1"));
You might notice that ForSyDeHierarchy
has many inner classes in it: they are not real classes, but shortcuts to the actual trait viewers
required to pull out this operation. That is to say, the line given is not the only way to achieve this but it is the recommended one.
We could also decide that v1
is actually an SDFActor
and also enforce
that is becomes one:
var v1Actor = ForSyDeHierarchy.SDFActor.enforce(m, v1);
Now v1
is being acessed as if it was a SDFActor
class! But in fact, we are “viewing” v1
under the lenses of the SDFActor
trait.
We could for example now change the production and consumption rates of v1
via the v1Actor
viewer; to Java, we are simply manipulating
an SDFActor
object,
v1Actor.addPorts("in", "out");
v1Actor.production().put("out", 1);
v1Actor.consumption().put("in", 2);
and we could save this again to check out the final product,
modelHandler.writeModel(m, "created_in_memory.fiodl");
if would want to query for vertexes of a certain ID, or find all vertexes that have a certain trait, we could do it with simple traversals and tryView
methods,
var query1 = m.queryVertex("IdontExist");
System.out.println(query1.toString());
var query2 = m.queryVertex("v1"); // I do exist :D
System.out.println(query2.toString());
// get all SDF actors
for (var v : m.vertexSet()) {
ForSyDeHierarchy.SDFActor.tryView(m, v).ifPreset(a -> {
System.out.println(a.toString());
})
}
note that most of the methods coming from ForSyDe IO are made to work out fine with Java 8+ Streams, which enable you to build near-declarative code at times,
specially when dealing with collection filtering and creation.
An important final word of wisdom where is the cross-cutting part of ForSyDe IO that might not have been evident in the foregoing tutorial.
A vertex can have many traits that define cross-cutting concerns on that vertex; let’s say, for example, that we will also add a Visualizable
trait, which
tell us that v1
should appear in a visualization framework later on:
ForSyDeHierarchy.Visualizable.enforce(m, v1) // if you don't want the resulting viewer
var v1visu = ForSyDeHierarchy.Visualizable.enforce(m, v1) // if you want it
var v1visu2 = ForSyDeHierarchy.Visualizable.enforce(v1Actor) // if you provide a viewer, you don't need to tell which system graph is being viewed.
You can then check that v1visu
and v1visu2
are in fact viewing the same underlying vertex!