Analyzing Java with Moose 8

Moose is a platform in Pharo that can manipulate models of software, to facilitate analyses including software data mining.

Posted by Christopher Fuhrman on July 29, 2019 · 15 mins read

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.

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!

Analysis overview

Here’s an overview of this post:

Overview of analysis process using Moose

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

Clone the Java project

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:

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 tmp/MooseEasyRepos/bethrobson__Head-First-Design-Patterns.

Note that 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

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 
      ifTrue: 'java command found.' 
      ifFalse: 'java command NOT FOUND.'
    
  • Download and unzip VerveineJ 1.0 with the following commands in a Moose Playground:
    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 VerveineJ-1.0.1.

Once you have VerveineJ, there are two ways to create the FAMIX model from the Java source code:

  1. Start the 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 tmp:
    Famix Maker Dialog
    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.

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

Load model of Java source (into Moose)

Load model of Java source

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

Visualize a Java package in PlantUML

Visualize model

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:

PlantUMLGizmoMoose Dialog

Click on the HFDP Moose model on the left to browse to the list of classes and interfaces from the source code.

PlantUMLGizmoMoose Dialog

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:

PlantUMLGizmoMoose Dialog

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:

PlantUMLGizmoMoose Dialog

Click Get the diagram to see the UML class diagram:

PlantUMLGizmoMoose Dialog

You can change the details of the diagram, to show Inheritance and Aggregation by clicking the respective check boxes.

PlantUMLGizmoMoose Dialog

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:

PlantUML class diagram

Perform a Moose analysis using Pharo

Perform analysis

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 Class in this output is from the Moose’s meaning, not Java’s meaning.

PlantUMLGizmoMoose Dialog

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.