Logo

Free the code from models !

Models are no longer restricted to modeling environments. The increasing use of Model@Runtime requires rich and efficient modeling frameworks.
KMF is developed to provide efficient modeling frameworks to edit, compose and synchronize models on Java and JavaScript Virtual Machines.


current version

1.9.2


Our main website has moved: Please jump to http://modeling.kevoree.org

About Kevoree Modeling Framework

KMF generates modeling frameworks and API for JS and JVM platforms, from a domain metamodel. It also offers comparison, merge and incremental synchronisation mechanisms tuned for Runtime use, making a powerful tool for server-side storage and presentation in a simple browser. In short, KMF generate business specific API and Tools from a metamodel, ready for distributed modeling activities.

Features

Let's take a concrete example

Concrete things are better to understand abstract modeling tools.

When you think about Cloud systems...

You think of hosting nodes and hosted software .

Everything begins with a metamodel

A cloud is composed by


A collection of nodes identified by an ID.

Each Node contains several software identified by their names.

Use any eclipse graphical or textual editor to produce an ecore metamodel.
See the XMI

Then KMF generates POJO *like* API

Java


//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);					
				

JS


//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);					
				

Easily access model elements using IDs and Paths

Java


//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]
				

JS


//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.

Load, Save and Clone your models in XMI or JSON

Java


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);
				

JS


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.

Use Events to stay informed

Java


//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");
				

JS


//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.

Set operators to merge and compare

Java


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
				

JS


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.

Synchronize your models incrementally !

Java


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);
				

JS


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.

Dive efficiently into models

Java


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);

				

JS


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.

Inject behavior and build executable model

Kotlin (JVM) and JS


//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


//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

And many more things...

Efficiency

KMF takes efficiency seriously.
With a generative approach, you can expect the best performance at runtime for your modeling operations.

Easy of Use and Portable

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.

Distributed

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.

Want to test it LIVE ? Try our playground .

Contribute

Want to help? We're on GitHub. Feel free to give us any feedback.

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 GitHub.

Need some help?

Start your own project and ask us on GitHub!