Moose is a platform in Pharo that can manipulate models of software, to facilitate analyses including software data mining. In this blog post, I will show you a few features of Moose to analyze a Java project.
Note: Although Pharo is supported and is stable in Windows 10, there can be some problems, especially with long directory paths and spaces in file names, that can occur in this tutorial. It’s possible to use Pharo in WSL and avoid these pitfalls. If you’re going to do a lot of work with Pharo in Windows, I highly recommend checking out WSL!
Here’s an overview of this post:
Moose operates on models of software, namely FAMIX models. To analyze a Java project, you must first create a model of it using a Java-to-Famix parser. In this example, I will use VerveineJ, but it’s also possible to use JDT2Famix.
PlantUML is used to create class diagrams of the software in Moose (this is optional). For this post, I installed a local version of the PlantUML server, which also requires an installation of GraphViz.
To make this post, I used Moose 8 in Pharo 8, both of which were in development stage at the time of writing this. Here’s a simple way to get it running:
Warning: Windows 10 users will want to remove the spaces from the name of the image, or else they may cause problems in the scripts in this tutorial. You can name the image Moose8JavaTutorial, for example.
In this step, let’s assume there is a GitHub repository of a Java project that we want to analyze, e.g., the source code from Head First Design Patterns.
In this step we will get a local copy of the source code using git clone, so
git needs to be installed on your machine and visible from the execution path.
MooseEasyUtility class will clone it in a temporary folder of the Pharo image directory.
Open a Moose Playground (CTRL+O+W) in Pharo, and execute the following:
javaProjectFileRef := MooseEasyUtility cloneGitHubRepo: 'https://github.com/bethrobson/Head-First-Design-Patterns'.
This will create a clone of the Java repo from GitHub in your Pharo working directory, with a relative path of
Note that we are not using
Icebergto make this clone, but a
git clonecommand run in a Bourne shell via
LibCin Pharo. We chose not to use Iceberg because the command runs faster, and there is no memory allocated in the Pharo image for the repository.
If you want to analyze Java source code, you can use any directory – it doesn’t have to be a git repository and you don’t have to create it using the above command.
In Pharo under Windows, you will briefly see a
cmd.exewindow appear during the execution of the command. This is a “gotcha” also discussed in the
Once we have a local copy (clone) of the source code, we can make the FAMIX model of it using a parser such as VerveineJ, which is supported by Moose-Easy. To install VerveineJ for our purposes, it’s simple:
javacommand) is in the execution path of your system. To verify, execute the following in a Moose Playground:
(LibC runCommand: 'java --version') = 0 ifTrue: 'java command found.' ifFalse: 'java command NOT FOUND.'
UIManager default informUserDuring: [ :bar | bar label: 'Downloading VerveineJ 1.0.1...'. [ | client | client := ZnClient new. client signalProgress: true; url: 'https://github.com/moosetechnology/VerveineJ/archive/v1.0.1.zip'; downloadTo: FileLocator imageDirectory. client isSuccess ifTrue: [ ZipArchive new readFrom: 'v1.0.1.zip'; extractAllTo: FileLocator imageDirectory. "Permissions may not be set with ZipArchive#extractAllTo:" "Note: This fails (silently) in a Windows VM" LibC runCommand: 'chmod u+x VerveineJ-1.0.1/verveinej.sh' ] ifFalse: [ self inform: 'Download failed.' ] ] on: HTTPProgress do: [ :progress | bar label: progress printString. progress isEmpty ifFalse: [ bar current: progress percentage ]. progress resume ] ]
If the download works, the VerveineJ importer will be in your Pharo working directory, with a relative path of
Once you have VerveineJ, there are two ways to create the FAMIX model from the Java source code:
FamixMaker tool in the menu Moose > Moose Tools > Famix Maker (or you can execute
MooseEasyFamixMakerPresenter open in a Moose Playground). You supply the paths to the source code, the VerveineJ parser script
verveinej.sh and the destination MSE (FAMIX) file. With the relative paths of the examples above, the Java source to parse is at
tmp/MooseEasyRepos/bethrobson__Head-First-Design-Patterns, the VerveineJ parser is at
VerveineJ-1.0.1/verveinej.sh and we choose the name
HFDP.mse to be the MSE file to be stored in
Click Generate MSE File when all the fields are correct. As before, in Windows you will see the
cmd.exe window and even the execution of a shell script.
Alternatively, use a programmatic interface. In the same Moose Playground where we cloned the source and VerveineJ parser above, invoke it like this:
wizard := MooseEasyFamixMaker generateMSETo: 'tmp/HFDP.mse' asFileReference parsing: 'tmp/MooseEasyRepos/bethrobson__Head-First-Design-Patterns' asFileReference with: 'VerveineJ-1.0.1/verveinej.sh' asFileReference. wizard generateMSE.
Either way, at the end of this step there should be a file
tmp/HFDP.mse that is the FAMIX model of the Java source code.
If you use the GUI, the following code is generated in the text box at the bottom of the dialog. Otherwise, you can copy it from here, changing the paths for the Java source and MSE files:
"Load the moose Model with some error checking" | mseFileRef mseStream mooseModel | mseFileRef := 'tmp/HFDP.mse' asFileReference. "Generated by FamixMaker" mseStream := mseFileRef readStream. mseStream ifNotNil: [ mooseModel := MooseModel importFromMSEStream: mseStream. mooseModel rootFolder: 'tmp/MooseEasyRepos/bethrobson__Head-First-Design-Patterns'. mooseModel install. "So it appears in the Panel" mseStream close. ] ifNil: [ self error: 'Could not load MSE file into Moose: ' , mseFileRef asString ].
The PlantUML Pharo Gizmo project has a GUI to visualize Moose models. You start the GUI with the following:
Invoke the GUI with the following command in a Moose Playground:
The following browser should appear:
Click on the
HFDP Moose model on the left to browse to the list of classes and interfaces from the source code.
In this example, we will focus on a particular package:
headfirst::designpatterns::combining::decorator. We can filter the list by adding the following code in the editor at the bottom:
each mooseName beginsWith: 'headfirst::designpatterns::combining::decorator'
Press Enter to accept the filter, and you should see a new list:
Select all the elements in the list by clicking in the list and typing CTRL+A. Then, right-click in the selected list and choose Select:
Click Get the diagram to see the UML class diagram:
You can change the details of the diagram, to show Inheritance and Aggregation by clicking the respective check boxes.
You can get a copy of the .png (or .svg) of the diagram by clicking the Copy Current UML Code button, and pasting the code in an editor such as PlantText.com. The following is an SVG version of the diagram generated by PlantUML Gizmo for Moose:
Moose combined with Pharo is very powerful mechanism to do analyses on software.
In this example, let’s assume we want to find all the Java classes in the Head First Design Patterns project that implement more than one interface.
It helps to understand that in Moose, a Java interface and a Java class are the same FAMIX element.
That said, a class element’s hierarchy can be obtained in several ways in Moose.
For now, we will consider the message
directSuperclasses, which in Moose returns the direct superclass (or superinterfaces) of a Java class (or interface).
As such, we can assume a class implements more than two interfaces if
directSuperclasses returns more than two elements. That is, the one (1) superclass of the Java class, and at least two (2) superinterfaces it also implements.
In a Moose Playground, type the following Pharo statements:
"Get the HFDP model (first in Moose panel)" javaModel := MooseModel root first. "Query all classes that have more than two direct FAMIX superclasses" classesImplementingMoreThanOneInterface := javaModel allModelClasses select: [ :each | each directSuperclasses size > 2 ]
Click Do it all and go (Ctrl+Shift+G) to see the list of classes that implement more than one interface.
Clicking on one of the results in the list, e.g.,
BeatModel (the first one), we can verify the results of the analysis, i.e., that the class implements at least two interfaces, by clicking the Raw tab in the window on the right and typing
self directSuperclasses in the text box at the bottom. Typing Ctrl+G (Do it and go) will show the list of elements for this message, which indeed includes two interfaces:
MetaEventListener in javax::sound::midi (Class) BeatModelInterface in headfirst::designpatterns::combined::djview (Class) Object in java::lang (Class)
Note the use of
Classin this output is from the Moose’s meaning, not Java’s meaning.
For more analyses, see The Moose Book.
Thanks to the
PlantUMLPharoGizmo tools shown in this post, we have shown a relatively easy way to analyze Java projects with Moose.
I am grateful to Professor Stéphane Ducasse and the entire RMoD team for their generosity during my 2018-2019 sabbatical at INRIA Nord Europe Lille, where I learned so much about Pharo, Moose and a productive team culture in open source software engineering.