MAMBA Quick Start
Quick Start Example
This quick start guide demonstrates the use of MAMBA for model-based analysis of a sample application's lines of code (LOC). A measurement provider is used to import raw measurement data from an external static analysis tool.
The resources used in this document can be found in the quickstart-example/ directory of the Git repository and the release archives.
Structure of this document:
- Section 1 introduces the models and the raw measurement file used in the examples.
- Section 2 demonstrates the use of MAMBA as a command-line tool.
- Section 3 describes the use of MAMBA's Java API.
- Section 4 explains how to implement a custom measurement provider.
1. Example Models and Raw Measurements
Bookstore Domain Model
As a running example system to be analyzed by MAMBA, we use a sample component-based application called Bookstore.
The Bookstore consists of three components, namely Bookstore, Catalog, and CRM, each providing a single software operation:
- Bookstore.searchBook() : List<Book>
- Catalog.getBook() : Book
- CRM.getOrders : List<Order>
The following object diagram depicts the domain model for this Bookstore system. The domain model is an instance of the Ecore-based meta-model system.ecore. The XMI serialization of the model, to be processed by MAMBA, is provided by the file bookstore.system.
SMM Model with LOC Measures
We are interested in the following lines of code (LOC) measures of the Bookstore's implementation:
- Each operation's LOC,
- Total LOC, assuming this to be the sum of the LOCs of each operation.
The following diagram depicts these measures, modeled as the SMM instance bookstore-analysis.smm.
Raw LOC Measurements
We assume that an external tool provides LOC measurements in a CSV file, with columns for component name, operation name, and its corresponding lines of code. The content of the example file quickstart-example/bookstore-locs.csv is listed below:
Bookstore;searchBook;20; Catalog;getBook; 10; CRM;getOrders;40;
2. Using MAMBA from the Command-Line
Now, we want to use MAMBA to compute an SMM output model for the above-described SMM input model bookstore-analysis.smm, based on the raw LOC measurements bookstore-locs.csv.
In order to execute the example, change to the directory quickstart-example/. Under UNIX-like systems, the following shell command, also included in the shell script run.sh, starts MAMBA with the example files. Under Windows, the colon ':' in the classpath string will need to be replaced by a semicolon ';'.
$ java -cp lib/mamba-core-1.0-SNAPSHOT-jar-with-dependencies.jar:lib/quickstart-1.0.0-jar-with-dependencies.jar \ mamba.mee.MeasurementApplication bookstore-analysis.smm LOC bookstore-output.smm bookstore.system \ SimpleCSV=simple.properties
The MAMBA command-line tool requires at least four arguments:
- An SMM instance which includes at least one measure to be applied to the domain model.
Here: bookstore-analysis.smm - A comma separated string with requested measures.
Here: "LOC" - A name for the resulting SMM file.
Here: bookstore-output.smm - One or more domain models to be analyzed.
Here: bookstore.system
Optionally, additional arguments can be used to pass configuration properties to measurement providers. Such arguments need to comply to the following pattern: <measurement provider prefix>=<properties file>: In our example, we pass the file simple.properties which points the used measurement provider to the file system locations including the raw LOC measurements:
simpleCSV.file=bookstore-locs.csv
In our example, MAMBA produces the SMM output file bookstore-output.smm. In addition to the contents of the input SMM instance, this file includes the SMM Measurements computed by MAMBA. The following listing includes the latter part. You can see NamedMeasurements which correspond to the different operations in the Bookstore model and they hold values which are the lines of code given in the CSV file. Furthermore, we have a CollectiveMeasurement which references the root node of the Bookstore model and which holds the value "70". It is easy to see that this is the Bookstore's total LOC.
<?xml version="1.0" encoding="ASCII"?> <core:SmmModel xmi:version="2.0" [...]> <libraries> [...] </libraries> <observations whenObserved="2012-01-27T14:16:32.603+0100" observer="voorn" tool="Mamba Execution Engine"> <observedMeasures measure="//@libraries.0/@measureElements.0"> <measurements xsi:type="measurement:CollectiveMeasurement" value="70" baseMeasurement="//@observations.0/@observedMeasures.1/@measurements.0 //@observations.0/@observedMeasures.1/@measurements.1 //@observations.0/@observedMeasures.1/@measurements.2"> <measurand href="bookstore.system#/"/> </measurements> </observedMeasures> <observedMeasures measure="//@libraries.0/@measureElements.1"> <measurements xsi:type="measurement:NamedMeasurement" value="20"> <measurand href="bookstore.system#//@components.1/@operations.0"/> </measurements> <measurements xsi:type="measurement:NamedMeasurement" value="10"> <measurand href="bookstore.system#//@components.0/@operations.0"/> </measurements> <measurements xsi:type="measurement:NamedMeasurement" value="40"> <measurand href="bookstore.system#//@components.2/@operations.0"/> </measurements> </observedMeasures> </observations> </core:SmmModel>
3. Using MAMBA in Your Java Application
In the next section, you will see how the MAMBA MeasurementController can be used in a Java application.
The code fragments are based on the MAMBA class MeasurementApplication.java, implementing the aforementioned command-line tool.
If you want to use MAMBA within your own application, you will only have to create and start an instance of the MeasurementController. The controller starts a new thread and begins to apply the SMM instance to the model scopes, while your application can do something else. If your application has to wait for the end of the computation, it can call waitForCompletion().
... final URI smmInstance = URI.createFileURI("</path/to/input.smm>"); final URI smmOutput = URI.createFileURI("</path/to/output.smm>"); final URI domainModel = URI.createFileURI("</path/to/domain.model>"); final Map<String, Properties> providerProps = new HashMap<String, Properties>(); // set provider properties ... ... // init and start the measurement controller final MeasurementController controller = new MeasurementController(smmInstance, new URI[]{domainModel}, smmOutput, providerProps); controller.waitForCompletion(); ... }
4. Implementing a Measurement Provider
We will use the SimpleCSVMeasurementProvider.java, defined in the quickstart example, to describe how to implement a custom measurement provider.
First of all, you will have to create a class which extends MambaMeasurementProvider:
public class SimpleCSVMeasurementProvider extends MambaMeasurementProvider { ... }
The following methods need to be implemented:
- getSupportedScopes(): Returns an array with all supported domain meta models.
- getProviderPrefix(): Returns a globally unique string which identifies the measurement provider.
- start(): The measurement controller will create a new thread that executes this method.
Optionally, your class can implement the postInitialization() method which will be called immediately after the provider got all information.
Let us have a closer look how the SimpleCSVMeasurementProvider implemented the methods.
The SimpleCSVMeasurementProvider only supports domain models of type SystemModel:
@Override protected Class<? extends EObject>[] getSupportedScopes() { final Class<? extends EObject>[] result = new Class[1]; result[0] = SystemModel.class; return result; }
Its identifier is "SimpleCSV".
@Override public String getProviderNamePrefix() { return "SimpleCSV"; }
In the postInitialization() method, the provider checks if the property "simpleCSV.file" is defined. Furthermore, it reads all model scopes and creates a mapping between the component names, the operation names, and the objects which represent the operations in the model scopes.
@Override protected void postInitialization() throws InitializationException { final String fileName = props.getProperty("simpleCSV.file"); if (fileName == null) { throw new InitializationException("Provide a file name in the properties file"); } this.file = new File(fileName); for (final EObject obj : modelScopes) { for (final Component comp : ((SystemModel) obj).getComponents()) { final Map<String, Operation> map = new HashMap<String, Operation>(); operations.put(comp.getName(), map); for (final Operation op : comp.getOperations()) { map.put(op.getSignature().getName(), op); } } } }
In the start() method, it reads the CSV file and pushes the results to the controller (this.provideMeasurementResult(MeasurementResult) method). Therefore, you have to create a MeasurementResult object which expects an Observation object.
Important: After your provider finished its work, it has to call the signalizeTermination() method.
@Override public void start() throws InterruptedException { final Date observedAt = new Date(); try { // start to read csv file final BufferedReader buf = new BufferedReader(new FileReader(file)); String str = buf.readLine(); while (str != null) { final String[] cells = str.split(";"); final Operation measurand = this.operations.get(cells[0]).get(cells[1]); final NamedMeasurement measurement = MeasurementFactory.eINSTANCE .createNamedMeasurement(); measurement.setValue(new BigDecimal(Long.valueOf(cells[2]))); measurement.setMeasurand(measurand); final Observation o = MeasurementFactory.eINSTANCE .createObservation(); final ObservedMeasure om = MeasurementFactory.eINSTANCE .createObservedMeasure(); o.setWhenObserved(observedAt); o.setTool("SimpleCSVProvider"); o.getObservedMeasures().add(om); om.setMeasure(measures.get(0)); om.getMeasurements().add(measurement); final MeasurementResult result = new MeasurementResult(o); this.provideMeasurementResult(result); str = buf.readLine(); } } catch (final IOException e) { throw new InterruptedException("Could not read file: " + file.getAbsolutePath()); } this.signalizeTermination(); }