You think of hosting nodes and hosted software .
//Creation of a Cloud instance
Cloud cloud = factory.createCloud();
//Creation of a Node
Node myAmazonEC2node = factory.createNode();
myAmazonEC2node.setId("EC2_0");
//Adding the node to the cloud
cloud.addNodes(myAmazonEC2node);
//Creation of a Software
Softwzre myNginx = factory.createSoftware();
myNginx.setName("SRV0");
//Attaching the software to the node
myAmazonEC2node.addSoftwares(myNginx);
//create a cloud
var cloud = factory.createCloud();
//create a node
var myAmazonEC2node = factory.createNode();
myAmazonEC2node.id = "EC2_0";
//add node to your cloud
cloud.addNodes(myAmazonEC2node);
//create a software
var myNginx = factory.createSoftware();
myNginx.name = "SRV0";
//attach the software to your new node
myAmazonEC2node.addSoftwares(myNginx);
//select your node by ID
Node ec2node = cloud.findNodesByID("EC2_0");
//or with XPath style
ec2node = cloud.findByPath("nodes[EC2_0]");
//then select a software
Software soft = ec2node.findSoftwaresByName("SRV0");
//or by path
soft=cloud.findByPath("nodes[EC2_0]/softwares[SRV0]");
//print the path of any element
System.out.println(soft.path(););
//print on console : nodes[EC2_0]/softwares[SRV0]
//select your node by ID
var ec2node = cloud.findNodesByID("EC2_0");
//or with XPath style
ec2node = cloud.findByPath("nodes[EC2_0]");
//then select a software
var soft = ec2node.findSoftwaresByName("SRV0");
//or by path
soft=cloud.findByPath("nodes[EC2_0]/softwares[SRV0]");
//print the path of any element
console.log(soft.path(););
//print on console : nodes[EC2_0]/softwares[SRV0]
Model elements selection using paths is way faster than iterating on collections.
Similarly to database IDs or NoSQL indexes, KMF uses hashtables to speed up the lookup of elements.
ModelCloner cloner = new DefaultModelCloner();
ModelSerializer jsonSaver = new JSONModelSerializer();
ModelLoader jsonLoader = new JSONModelLoader();
ModelSerializer xmiSaver = new XMIModelSerializer();
ModelLoader xmiLoader = new XMINModelLoader();
//Save in standard XMI Format (work Eclipse)
System.out.println(xmiSaver.serialize(myCloud));
//Save in human readable JSON
System.out.println(jsonSaver.serialize(myCloud));
//Load from JSON and take first package (only one here)
Cloud cloud = jsonLoader
.loadModelFromString(txtPayLoad).get(0);
//Load from XMI
Cloud cloud = xmiLoader
.loadModelFromString(txtPayLoad).get(0);
//Clone the entire model
Cloud clonedModel = cloner.clone(cloud);
//Clone to a readonly structure
clonedModel = cloner.clone(cloud,true);
//Only clone a part of model, share the first node
cloud.getNodes().get(0).setRecursiveReadOnly();
Cloud clonedModel = cloner.cloneMutableOnly(cloud);
var cloner = new DefaultModelCloner();
var jsonSaver = new JSONModelSerializer();
var jsonLoader = new JSONModelLoader();
var xmiSaver = new XMIModelSerializer();
var xmiLoader = new XMINModelLoader();
//Save in human readable JSON
console.log(jsonSaver.serialize(myCloud));
//Load from JSON and take first package
var cloud = jsonLoader
.loadModelFromString(txtPayLoad).get(0);
//Clone the entire model
var clonedModel = cloner.clone(cloud);
//Clone to a readonly structure
var clonedModel = cloner.clone(cloud,true);
//Only clone a part of model, share the first node
cloud.getNodes().get(0).setRecursiveReadOnly();
var clonedModel = cloner.cloneMutableOnly(cloud);
//save into XMI format
var savedModel = xmiSaver.serialize(cloud);
//reload from XMI stream
var reloadCloud = xmiLoader
.loadModelFromString(savedModel);
We support the standard XMI interchange format for compatibility with other modeling framework tool like EMF.
In addition KMF offers to serialize your models in JSON format for more portability, especially designed for web usage.
KMF allows to set part of the model as Read-Only. With this feature, the clone operator is able to efficiently clone mutable elements only.
This ability drastically speeds up the cloning and enables processes of clones in parallel.
//Create a listener
class ModelSyncListener implements ModelTreeListener {
@Override
public void elementChanged(ModelEvent event){
switch(event.getType()){
case ActionType.ADD:
System.out.println("Add "+event);
break;
case ActionType.SET:
System.out.println("Set "+event);
break;
}
}
}
//Add it as TreeListener
//Listens this element + all contained
cloud.addModelTreeListener(new ModelSyncListener());
//Std listener only listen local event
//cloud.addModelListener(new ModelSyncListener());
Node node = factory.createNode();
node.setID("EC1");
//Trigger event ADD
cloud.addNodes(node);
//Trigger event SET
node.setId("EC2_1");
//Declare events listener
var listener = { elementChanged : function(event){
switch(event.getType()){
case ActionType.$ADD:
console.log("Add event ! "+event);
break;
case ActionType.$SET:
console.log("Set event ! "+event);
break;
}
}};
//Add as Tree (meaning global) listener
cloud.addModelTreeListener(listener);
//Std listener only listen local event
//cloud.addModelListener(listener);
var node = factory.createNode();
node.id = "EC1";
//Trigger event ADD
cloud.addNodes(node);
//Trigger event SET
node.id = "EC2_1";
Events are triggered each time an attribute or a relation of a model element is edited.
They make it easier to synchronize a presentation layer like a DOM fragment and the modeling layer.
ModelCompare compare = new DefaultModelCompare();
//Produces the trace sequence to go from MO to M1
TraceSequence diffSeq = compare.diff(model0,model1);
//Produces the trace sequence of MO inter M1
TraceSequence interSeq = compare.inter(model0,model1);
//Produces the trace sequence of MO merged with M1
TraceSequence mergeSeq = compare.merge(model0,model1);
for(trace : diffSeq.getTraces()){
//Prints the difference between M0 and M1
System.out.println(trace);
}
//Merges M1 into M0
mergeSeq.applyOn(model0);
assert(model1.modelEquals(m0));
Cloud model3 = factory.createCloud();
interSeq.applyOn(model3);
//model3 is now the common part of M0 and M1
var compare = new DefaultModelCompare();
//Produce the trace sequence of MO -> M1
var diffSeq = compare.diff(model0,model1);
//Produce the trace sequence of MO inter M1
var interSeq = compare.inter(model0,model1);
//Produces the trace sequence of MO merged with M1
var mergeSeq = compare.merge(model0,model1);
for(i=0;i < diffSeq.getTraces().$size;i++){
//Print the comparison between M0 and M1
console.log(diffSeq.getTraces()[i]);
}
//Merge M1 into M0
mergeSeq.applyOn(model0);
assert(model1.modelEquals(m0));
var model3 = factory.createCloud();
interSeq.applyOn(model3);
//model3 is now the common part of M0 and M1
Each set operator manipulates models as trace sequences. A Trace is defined as an atomic operation modifying a model (add, set, remove).
Trace sequences can be applied on models, making it possible to build any operators using these atomic steps.
Cloud cloudM0 = factory.createNode();
Cloud cloudM1 = factory.createNode();
ModelCompare compare = new DefaultModelCompare();
Event2Trace conv = new Event2Trace(compare);
cloudM0.addModelTreeListener(new ModelTreeListener(){
@Override
public void elementChanged(ModelEvent event) {
TraceSequence traceSeq = conv.convert(event);
//send via network for remote synchronisation
//traceSeq are serialiable
traceSeq.applyOn(cloudM1);
}
});
//Add a new node to cloudM0
Node newNode = factory.createNode();
newNode.setId("newNode")
cloudM0.addNodes(newNode);
//Now CloudM1 has also create one node
assert(cloudM1.findNodesById("newNode") != null);
var cloudM0 = factory.createNode();
var cloudM1 = factory.createNode();
var compare = new DefaultModelCompare();
var conv = new Event2Trace(compare);
var listener = { elementChanged : function(event){
var traces = apply.convert(event);
//send trace to network or browser events
//for remote synchronization
traces.applyOn(cloudM1);
}};
//Add a new node to cloudM0
var newNode = factory.createNode();
newNode.setId("newNode")
cloudM0.addNodes(newNode);
//Now CloudM1 has also create one node
assert(cloudM1.findNodesById("newNode") != null);
To synchronize several model instances simply capture events, convert them to trace, broadcast, and apply on each models.
Coupled with a conflict resolution mechanism, this makes the foundation for collaborative distributed modeling.
ModelVisitor visitor = new ModelVisitor(){
public override fun visit(
KMFContainer elem,
String refNameInParent,
KMFContainer parent) {
System.out.println(elem.path())
}
}
//recursive navigation into contained elements
cloud.visit(true,true,false);
//recursive navigation into not contained references
cloud.visit(true,false,true);
//Non recursive navigation into cloud references
cloud.visit(false,true,true);
ModelAttributesVisitor visitorAtts
= new ModelAttributesVisitor(){
public override fun visit(
Object attValue,
String name,
KMFContainer parent) {
System.out.println("Visit "+name)
}
}
//Visit all attributes of Cloud
cloud.visit(visitorAtts);
var visitor = new ModelVisitor();
visitor.visit = function(modelElem){
console.log(modelElem.metaClassName());
}
//recursive navigation into contained elements
cloud.visit(true,true,false);
//recursive navigation into not contained references
cloud.visit(true,false,true);
//Non recursive navigation into cloud references
cloud.visit(false,true,true);
var visitorAtts = new ModelAttributesVisitor();
visitorAtts.visit = function(attValue,name,parent) {
console.log(name);
}
//Visit all attributes of Cloud
cloud.visit(visitorAtts);
The Visitor API replaces the natural iterative navigation through model elements. You can choos to visit recursively or not (first boolean parameters). Visit methods are generated to offer best performances. In addition users can stop the recursion at anytime by using the stopVisit() methods inside the visitor object to globally stop the process. Also, noChildrenVisit() skips children.
//declare a new aspect on Node
aspect trait NodeAspect : Node {
//private method
//are not include in model
private fun privateProcess(){};
//method add to Node metaClass
fun newService(){}
}
//Add a new concept named ExNode
//The new concept will inherit Node
metaClass("ExNode") ExNodeA : Node {
//public method
fun ExRun(){
}
}
//create an instance of the concept
var newNode = factory.createExNode();
//JS version can use static aspects
//same as the JVM version.
//In addition model elements
//are generated extensible dynamically
//by leveraging prototypes
//add a method to the Node concept
factory.createNode().prototype.prettyPrint =
function prettyPrint(){
console.log(this.metaClassName());
}
//create a standard node object
var node = factory.createNode();
//call aspect method
node.prettyPrint();
Aspects replace the derived method of ecore. Allowing to write statically verified code instead of ugly putting code in
KMF takes efficiency seriously.
With a generative approach, you can expect the best performance at runtime for your modeling operations.
Unlike other modeling framework, KMF provides cross compilation for Java and JS platforms. Moreover it doesn't need any library @Runtime and is easy-to-use for any OO developper by leveraging simple POJO api.
Collaborative and distributed modeling is enabled by KMF thanks to the incremental synchronization mechanism.
It is now possible to embed and synchronize your models on tablets, phones and your home computer.
Free Software
We're researchers from Interdisciplinary Center for Security, Reliability and Trust (SnT), University of Luxembourg and INRIA Rennes (FR).Join the team
Help to build KMF on .Need some help?
Start your own project and ask us on GitHub!