Updated 2021-03-15
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.
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!
Analysis overview
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.
Install Moose
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:
- Install the Pharo Launcher and start it.
- Important for Windows 10: click on the VMs button in Pharo Launcher and click the Update button to make sure you have the latest (most stable) Pharo virtual machines.
- Create a copy of the image of Moose-8 from the Inria CI: New Image Templates > Official distributions > Moose Suite 8.0 (development version) > Create image
> 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. - Launch the image once it has downloaded.
Clone the Java project you want to analyze
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. The 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:
MooseEasyUtility cloneGitHubRepo:
javaProjectFileRef := '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 tmp/MooseEasyRepos/bethrobson__Head-First-Design-Patterns
.
We are not using Iceberg
to make this clone, but a git clone
command run in a Bourne shell via LibC
in 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.exe
window appear during the execution of the command. This is a “gotcha” also discussed in the LibC
post.
Parse Java to make FAMIX model
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:
- Make sure a Java Runtime Environment (
java
command) is in the execution path of your system. To verify, execute the following in a Moose Playground:
LibC runCommand: 'java --version') = 0
('java command found.'
ifTrue: 'java command NOT FOUND.' ifFalse:
- Download and unzip VerveineJ 1.0 with the following commands in a Moose Playground:
UIManager default
informUserDuring: [ :bar | 'Downloading VerveineJ 1.0.1...'.
bar label:
[ | client |ZnClient new.
client :=
clienttrue;
signalProgress: 'https://github.com/moosetechnology/VerveineJ/archive/v1.0.1.zip';
url: FileLocator imageDirectory.
downloadTo:
client isSuccessZipArchive new
ifTrue: [ 'v1.0.1.zip';
readFrom: FileLocator imageDirectory.
extractAllTo:
"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' ]
self inform: 'Download failed.' ] ]
ifFalse: [ HTTPProgress
on:
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 VerveineJ-1.0.1
.
Once you have VerveineJ, there are two ways to create the FAMIX model from the Java source code:
Start the
FamixMaker
tool in the menu Moose > Moose Tools > Famix Maker (or you can executeMooseEasyFamixMakerPresenter open
in a Moose Playground). You supply the paths to the source code, the VerveineJ parser scriptverveinej.sh
and the destination MSE (FAMIX) file. With the relative paths of the examples above, the Java source to parse is attmp/MooseEasyRepos/bethrobson__Head-First-Design-Patterns
, the VerveineJ parser is atVerveineJ-1.0.1/verveinej.sh
and we choose the nameHFDP.mse
to be the MSE file to be stored intmp
:
Click Generate MSE File when all the fields are correct. As before, in Windows you will see thecmd.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:
MooseEasyFamixMaker
wizard := 'tmp/HFDP.mse' asFileReference
generateMSETo: 'tmp/MooseEasyRepos/bethrobson__Head-First-Design-Patterns' asFileReference
parsing: 'VerveineJ-1.0.1/verveinej.sh' asFileReference.
with: 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.
Load model of Java source (into Moose)
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 |'tmp/HFDP.mse' asFileReference. "Generated by FamixMaker"
mseFileRef :=
mseStream := mseFileRef readStream.
mseStream
ifNotNil: [ MooseModel importFromMSEStream: mseStream.
mooseModel :=
mooseModel rootFolder:'tmp/MooseEasyRepos/bethrobson__Head-First-Design-Patterns'.
"So it appears in the Panel"
mooseModel install.
mseStream close. ]self error:
ifNil: [ 'Could not load MSE file into Moose: ' , mseFileRef asString ].
Visualize a Java package in PlantUML
The PlantUML Pharo Gizmo project has a GUI to visualize Moose models. You start the GUI with the following:
Click Moose > Moose Projects > Load PlantUML Gizmo to load the project.
Invoke the GUI with the following command in a Moose Playground:
PUGizmoForMoose open.
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:
'headfirst::designpatterns::combining::decorator' each mooseName beginsWith:
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:
Perform a Moose analysis using Pharo
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)"
MooseModel root first.
javaModel := "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)
The use of Class
in this output is from the Moose’s meaning, not Java’s meaning.
For more analyses, see The Moose Book.
Conclusion
Thanks to the Moose-Easy
and PlantUMLPharoGizmo
tools shown in this post, we have shown a relatively easy way to analyze Java projects with Moose.
Acknowledgements
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.