PIPE2 Report
PIPE2 Report
PIPE2 Report
FINAL REPORT
Edwin Chung
Tim Kimber
Ben Kirby
Thomas Master
Matthew Worthington
Supervisor: Dr W. Knottenbelt
Submission Date: 19th March 2007
Contents
II DEVELOPMENT 12
3 Refactoring and Code Clean Up 13
3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Bug Fixing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3 Integration of External Contributions . . . . . . . . . . . . . . . 14
3.4 Code Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.4.1 XML and DataLayer Division . . . . . . . . . . . . . . . . 15
3.4.2 Matrix Instantiation . . . . . . . . . . . . . . . . . . . . . 16
3.4.3 Reflection and Class Loading . . . . . . . . . . . . . . . . 17
3.4.4 Data Mismanagement . . . . . . . . . . . . . . . . . . . . 18
3.4.5 Evalution of refactoring . . . . . . . . . . . . . . . . . . . 19
4 Zoom Functionality 21
4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2 Research . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2.1 Existing PIPE code . . . . . . . . . . . . . . . . . . . . . 21
4.2.2 Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.2.3 Successful Approaches to Zoom Implementation . . . . . 22
4.3 Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.3.1 Design Decisions . . . . . . . . . . . . . . . . . . . . . . . 24
4.3.2 Geometrical Approach . . . . . . . . . . . . . . . . . . . . 24
4.3.3 Code Architecture . . . . . . . . . . . . . . . . . . . . . . 25
4.4 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.4.1 PlaceTransitionObjects . . . . . . . . . . . . . . . . . . . 27
4.4.2 Arcs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.4.3 Labels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2
4.4.4 AnnotionNotes . . . . . . . . . . . . . . . . . . . . . . . . 28
4.4.5 Updating the JScrollPane . . . . . . . . . . . . . . . . . . 28
4.4.6 Click and Drag . . . . . . . . . . . . . . . . . . . . . . . . 29
4.4.7 User Interface to Zoom Functionality . . . . . . . . . . . . 30
4.5 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.5.1 Differences from the Specification . . . . . . . . . . . . . . 33
4.5.2 Key Metrics Usability and Reliability . . . . . . . . . . . 34
6 Testing 51
6.1 Library Integration . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.2 Implementation of Testing Framework . . . . . . . . . . . . . . . 52
6.3 PetriNet Specific Testing . . . . . . . . . . . . . . . . . . . . . . 53
6.4 Evaluation: Test Coverage . . . . . . . . . . . . . . . . . . . . . . 53
9 Future Directions 57
9.1 Hierarchical Nets . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
9.2 Copy and Paste . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
9.3 Undo and Redo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3
Acknowledgements: Thanks to Dave Patterson for offering insight and ad
vice; to John Mocenigo at AT&T for allowing use of AT&T’s hosted GraphViz
service during project development; and to Dr William Knottenbelt for his sup
port as project supervisor.
4
Part I
INTRODUCTION AND
PLANNING
5
1 Introduction
The aim of this project was to enhance an existing piece of software written for
a 2002/3 MSc Conversion group project[3], and subsequently extended by MSc
students in 2003/4[1] and 2004/5[2]. The application, PIPE (Platform Inde
pendent Petri net Editor), is a Javabased editing and analysis system for Petri
nets. It has been improved by eradicating bugs, refactoring code to make it more
efficient and understandable, and by adding two major pieces of functionality.
6
Figure 1: Dijkstra’s Dining Philosopher’s problem
7
1.2 PIPE
The figure above shows a Petri net representing Dijkstra’s wellknown Dining
Philosopher’s problem[4]. Places are shown by circles and transitions by rectan
gles, connected by directional arcs. The oddnumbered central places represent
the forks shared resources which each philosopher requires two of in order to
eat. In its initial state, all evennumbered transitions are enabled and equally
likely to fire. If, for example, T2 was to fire, the tokens at P7, P8 and P9 would
be destroyed, and a token at P10 would be generated by transition T2. This
would have the effect of removing two resources (forks) from the central pool,
thus limiting access by the other philosophers. If T3 were then to fire, the token
at P10 would be destroyed and tokens at P7, P8 and P9 would be generating,
returning the system to its initial state. This Petri net is bounded and safe
(i.e. each place can contain no more than one token); however it is possible to
reach a state of deadlock (indeed, this is the issue that the Dining Philosophers
problem serves to illustrate).
2.2 Familiarisation
Most major updates to the application have been made on an annual basis by
groups of students, who then left Imperial College. This meant that there were
some issues with continuation of knowledge: new groups come to work on PIPE
with no prior knowledge of the application, yet the previous year’s developers,
who had come to understand the PIPE’s design and inner workings quite inti
mately, had by this point moved on. Even detailed documentation left in the
annual reports and in source code comments provides no substitute for faceto
face interaction. Therefore, before any actual development work could begin,
8
a substantial amount of time was spent becoming familiar with the applica
tion. This involved studying code, reading what documentation was available,
communicating with contributors to the PIPE project beyond Imperial College,
generating UML diagrams for the software, and then attempting to fix bugs (see
section 3.2).
The key areas identified for this year’s work therefore were: refactoring and
code cleanup; incorporation of a module that would allow users to generate
reachability graphs; and adding zoom functionality.
Additionally, to address the issues of code complexity and maintainability we
proposed to introduce an automated testing framework to the project (at this
stage no tests existed within PIPE2). A well written suite of automated tests
would allow developers to refactor and add to the code with confidence that
new bugs were being kept to a minimum. Over time, this should allow faster,
better designed improvements to the application. Another important benefit of
automated testing and a continously integrated build is that testing and bug
fixing is spread over the life of the project, rather than being concentrated into
the period just before release. Testing is described fully in section Finally, we
considered the possibility of improving options functionality. Many options that
would be useful to specify were defined all over the code and could not be
changed dynamically. A single properties file with all of the options (such as
9
the colour of active transitions and the size of annotation text) would improve
PIPE’s usability.
10
The chart features key milestones of completing reports and making releases
of the application. The Gantt chart was updated at intervals as the project pro
gressed, making it possible to see whether tasks were being completed on time.
The effectiveness of group organisation and project management is discussed in
the Evaluation section.
• It is free
• It is well supported under Windows, Linux and Mac OS
• It has a highly mature Java environment
• It has native support for CVS
• It was easily able to import the existing code tree and compile it
11
Part II
DEVELOPMENT
12
3 Refactoring and Code Clean Up
3.1 Introduction
The decision to develop PIPE within the framework of the open source com
munity has yielded substantial benefits to the application as a whole and to
the user base in general. The direction and focus of the application’s software
development has often been driven by user demand for specific functionality.
This has enabled the software to evolve into a highly usable and diverse toolset
for creating and analysing Petri nets. It successfully addresses the requirements
and obligations of real world users who stress the system on a daily basis. Nev
ertheless, as a result of the demand orientated approach to development and
the dynamic nature of the core group of developers over the last five years (con
stantly changing on a yearly basis), the code itself rather than its functionality
has become a fundamental concern of the developers. In order to sustain long
term projects, one of the key issues is building a sturdy foundation on which
future generations of coders can naturally develop viable extensions. Unfortu
nately, this has not been a priority and over the last couple of years a certain
amount of inefficiency has slowly crept into the code base. As a natural conse
quence of the project’s rapid expansion, some of these inefficiencies are caused
not by individual modules but rather by the complexity introduced as a result of
multifarious interaction between classes. In addition, the lack of a clear develop
ment strategy from the outset has resulted in poor management of data and an
unnecessary repetition of common processes. Understandably, as separate years
of developers implement increasing levels of functionality, the logical separation
that usually exists between classes has become indistinct and has negatively
impacted the application’s efficiency. Mostly, this part of the work involved the
refactoring described in section 3.4. However, before this could take place, there
were some outstanding bugs which needed attention, and some code which had
been modified by a developer working in isolation (outside the CVS system)
needed to be integrated.
• Tim fixed one of the bugs listed in the original report (Infinite while
(!(isEmptySet(chj))) loop in the invariant analysis).
• During the course of his refactoring work, Edwin discovered and fixed
four bugs including one posted by Tim (it was causing animation to freeze
13
halfway while stepping back through a long firing sequence). Edwin man
aged to fix it by modifying the code in several places to resolve the discrep
ancies between the count int variable and the firedTransitions arrayList,
which are used to play back the firing sequence. There was a bug where
users were not prompted to save when they added or removed tokens from
the net. Edwin fixed the problem by adding a code in the setCurrentMark
ing() method in the Place class so that it will set the netChanged to true
whenever its number of tokens is altered. There was a problem where
users could still step forward a previous firing sequence after it had been
reset (e.g. toggling off and on the animation mode). This was resolved
by changing the code in the GuiFrame class so that it resets the fired
Transitions array list and the counter in the Animator class. Finally, the
animation didn’t playback the way it was expected to when the firing se
quence has been altered i.e. firing a transition in the middle of the firing
sequence. Edwin found out that the problem was due to the application
trying to simulate the original firing sequence which was still in memory.
He changed the code in the animator class so that whenever the firing se
quence is altered, all successive stored transitions in the previous sequence
are pruned. He also made corresponding changes in the AnimationHistory
class so that the correct firing sequence is displayed.
• One of the unfortunate truths for any development team working on re
vamping a particular code base is that some errors will be inherited from
the original code. One such instance was a particularly obscure problem
relating to the saving of Petri nets. At some point during the construction
of a net, the core representation of the net was becoming corrupted and
this naturally led to a plethora of unexpected behaviour and errors. The
first signs of this problem were raised when nets that had been worked on
for extensive time were causing a null pointer exception when attempt
ing to be saved. With focussed testing the group realised that this was
only the tip of the iceberg, and that should the exception be generated, a
variety of unexpected behaviour was observed throughout the entire appli
cation. This was eventually tracked back to a problem with the manner in
which objects were being created according to mouse presses and releases.
Changes made by Matt to the manner in which the object handler dealt
with events within the container which resolved many of the related issues.
Finding a solution was far simpler than finding the cause, highlighting the
need for welldesigned exception handling.
14
of new functionalities including cut and paste, redo and undo etc to the end of
the project, if time permits. This was mainly due to time constraints and the
possibility that Pere’s code would introduce conflicting changes to the work we
set out to do. We have created a separate branch on the CVS server to facilitate
the merging of the two versions in the future.
3.4.1.2 Solution In order to solve the problem, Ben spent time investigat
ing the existing code and identifying the methods that dealt with saving and
loading. He then determined how best these could be abstracted to different
classes. In order to better understand the code and possible solutions, he spent
time investigating the handling of exceptions in Java, and also the use of cer
tain design patterns, as suggested by team mates, such as the ’Factory’ and
’Builder’ patterns. The saving functionality was easier to abstract, as the Data
Layer object doesn’t have to to be manipulated. The method savePNML, and
the methods createPlaceElement, createAnnotationElement, createArcElement,
createArcPoint and createTransitionElement, which are used to create the ele
ments in the XML Document object, were all abstracted to a DataLayerWriter
class, along with the necessary imports. savePNML now takes in the Data
Layer object to save, and the other methods examine this when building the
Document. Ben then changed the call to savePNML in the saveNet method
of the GuiFrame class, so that a DataLayerWriter object is created, and the
15
method called from this. The loading functionality was more difficult to ab
stract, as the code for loadPNML involved parsing and transforming an XML
file, cycling through its elements, determining their nature (Place, Transition,
etc) and then adding the necessary attribute to the DataLayer object. This
last piece of functionality was difficult, and seemingly inappropriate, to per
form in a class other than DataLayer itself, as the data members and methods
needed are private. A first attempt was to abstract all the necessary methods
from Data Layer, however these methods are needed in order to add to a Petri
net model when using the application, and so would have to be duplicated. A
suggestion from a team mate was to implement an interface containing these
methods. However upon discussion it was decided that this would increase the
coupling between classes, and would also leave the problem of a class trying
to modify the private data members of another, which seems especially inap
propriate when the object in question is actually being created. Therefore Ben
decided to split the functionality of loadPNML. The actions concerning XML
were removed to a separate class PNMLTransformer, which includes the func
tion transformPNML. This takes the filename of the XML file and parses and
transforms the file, creating a Document object, which is then passed back to the
method’s call in the DataLayer constructor. A separate method in DataLayer,
createFromPNML, was then written, which takes this Document and uses the
DataLayer class’s original methods for modifying a net model to create the one
described in the XML file. All the getDOM methods were also abstracted to
the PNMLTransformer class as these involve the use of XML and returning a
document. Finally, Ben modified the create NewTab method in GuiFrame so
that instead of calling loadPNML from the present DataLayer object, a new
instance of PNMLTransformer was created, and the methods called appropri
ately. The second abstracted class was called PNMLTransformer rather than
DataLayerFactory as initially anticipated because Ben felt the new class didn’t
fit the standard ’Factory’ pattern, in that it didn’t involve a condition and then
a call to build an object, but rather was just a class concerned with the parsing
and transformation of XML files.
16
was impact to the analysis. For example, when a model was created from an
XML file, the incidence matrices could be created once and reused until the user
changed the number of tokens at a place, or changed the number or kinds of
arcs, places or transitions. Aside from iterative simulation, all of the analysis
modules ran on data that was static as far as the structure goes, yet the analysis
routines were all written to keep generating the incidence matrices repeatedly.
We investigated ways of reducing unnecessary updates to the data model used
in the analysis modules, perhaps by developing methods which could monitor
when an impactful change has been made to a Petri net and flag this up to
the analysis module, which would only then update the model (or parts of the
model) it is using for the analysis.
3.4.2.2 Solution Edwin addressed this issue, refactoring the code to make
it more speed and memory efficient. He added a new matrixChanged Boolean
attribute to the matrix object and modified the code so that matrices are only
recreated when they have been altered.
3.4.2.3 Evaulation Comparing the modified code with the original code,
it can be shown that the number of times the createMatrix method is called is
reduced by half when the GSPN, invariant and incident markup analyses were
performed on the Petri net.
17
was hard coded into property files (the configuration files). The properties were
then streamed out of the files providing the classes with both a correct name
and file path to the actual ".class" files. Each class was then loaded via the
ModuleLoader and the ExtFileManager using a custom classloader that fell un
der the hierarchy of the urlClassloader. The methods of these classes were then
used to populate the "available module" tree at runtime.
This seemed to contradict the idea of pluggable modules which could sim
ply be dropped into the module folder upon completion and be immediately
integrated into the project. It also introduced complications should modules be
either moved or renamed. Changes would involve updating each of the individual
property files for all the modules. Modifying the getModuleClasses/getModuleDir,
Matt made this loading more pluggable, so that the class names and paths were
no longer hard coded but truly dynamic. By recursively searching through the
modules directory and sub directories and filtering the results through an exten
sion filter, it was possible to load all the classes from that particular directory
regardless of the contents, picking up only modules that successfully imple
mented the module interface. The ModuleLoader class was changed to reflect
this different approach and a getClassName method dealt with generating Class
names. Thus modules can now be dropped into the module folder and simply
incorporated into the build without the need for modifying configuration files.
3.4.4.2 Solution Will identified numerous instances where arrays were be
ing created in order to facilitate basic parts of the analysis. This was particu
larly prevalent in the Classification class, and also the GSPN class which extends
Classification. The initial solution was to introduce local placeholders within the
Classification class, one for Place arrays and one for Transition arrays. These
were set up so that on the first attempt to access, a copy was made; this copy
was then used for subsequent accesses by analysis modules. Although this didn’t
produce any perceptible issues when the application was run, it turned out that
the changes were causing a test to fail. The test had been written by Edwin
to ensure some basic requirements of Petri nets were being met. Will therefore
used an alternative method to prevent unnecessary creation of arrays methods
18
were written to get the counts of elements in the Place and Transition arrays
used by modules. Since so much of the inefficient code involved creating arrays
just to then get their length, this should significantly improve the efficiency of
the code. The new methods were added in the DataLayer class, and a total of
23 references in five different classes were updated to take advantage of the new
methods.
19
20
4 Zoom Functionality
4.1 Introduction
As outlined in the specification of the zoom function, the idea was to add this
feature to PIPE to give users greater flexibility in working with their Petri nets.
At a functional level, what one would want from zoom is fairly obvious. So,
when planning the work, possibly the most important considerations to keep in
mind regarded avoiding unwanted side effects of adding zoom. In particular,
use of the zoom function should not:
4.2 Research
4.2.1 Existing PIPE code
The existing version of PIPE was run in debug mode, using the Eclipse IDE, to
determine the way changes and additions to the Petri net diagram are carried
out. The case of adding a new place to the net was chosen as a simple example
to look at. Single stepping through the code and recording the flow of control
enabled the UML sequence diagram in Figure 4.1 to be produced. The diagram
shows that, although PIPE does not follow the MVC pattern fully by separat
ing the model and the view of a place into separate classes, it does separate
the interactions the Place object is involved in. At the top of the diagram the
21
newly created Place object is added to a DataLayer object which is responsible
for providing information when analyses are run, or the net is saved to file. In
the lower half of the diagram the Place is added to a GuiView object which is
the onscreen component that displays the Petri net. From this we were able to
establish that most of our work would need to look at the representation of the
net by the GuiView, and the interactions between the user and the GuiView.
The only point to be addressed on the model side would be to ensure that the
x and y values of each component provided to the DataLayer, and subsequently
saved to file, were not affected by the current zoom.
4.2.2 Swing
The GUI components used in PIPE are part of the Java Swing library, and so
several of the Java Tutorial pages related to Swing were looked at as part of
our research. The most important information we found related to the layout of
components within a container[6], and transforming shapes and images[7]. Lay
ing out components within a container in Java is controlled by a LayoutManager
object, which pulls, pushes, stretches and squeezes components into a particular
overall layout, e.g. a grid of cells where the components are all forced to be the
same size. In fact, PIPE bypasses this system by setting the LayoutManager
of the GuiView container to null and delegating positioning and sizing to each
individual component. They achieve this using pixel measurements, relative to
the top left corner of the parent GuiView in the case of positioning. Scaling (or
translating, shearing or rotating) can be applied to graphics in Java using the
Java.awt.geom.AffineTransform class. An AffineTransform object can be passed
to the Graphics2D object that shapes and images are painted into, causing the
graphics to be transformed in some chosen fashion. In our case, we would cre
ate an AffineTransform and use its scale() method to set the degree of zoom we
want to apply to the shapes of the Petri net.
22
Figure 4: UML Sequence diagram for adding a new place to the Petri Net. The
Place is registered with both a DataLayer and a GuiView.
23
Figure 5: Zooming from the centre out
4.3 Design
4.3.1 Design Decisions
We posed the following questions when thinking about how zoom would work:
24
Figure 6: Zooming from the container origin. The diagram is subsequently
scrolled, so that it appears as though the screen is zooming in on the central
point.
would be necessary to use these two steps to achieve the correct expansion of
the diagram around the central point. However, once implementation began it
was realised that this was very complicated to get right. Furthermore, simply
multiplying all the normal x and y coordinates by the zoom factor, and then
scrolling to the correct point was equivalent to the more complicated approach,
and achieved exactly what we wanted. See Fig. 4.3.
25
Figure 7: Sequence diagram for zoom. The GuiView makes a zoomUpdate()
call on all its Zoomable components, and the ZoomController is queried for the
current settings.
26
Figure 8: UML class diagram showing the relationships between GuiView and
some of the Zoomable and nonZoomable components it may contain. Not
all classes derived from PetriNetObject are shown. Several others, like Arc, did
not need to implement Zoomable, so it was decided not to make PetriNetObject
Zoomable itself.
4.4 Implementation
4.4.1 PlaceTransitionObjects
Places and Transitions in PIPE both derive from PlaceTransitionObject and
much of the implementation, including the zoomUpdate() method was done at
the superclass level. Firstly, two new variables, locationX and locationY were
added to the class to store the ’real’ coordinates of the object, i.e. with no zoom.
These variables are only updated if the object is actually selected and moved by
the user, not if it moves as the result of a zoom. Next, zoomUpdate() was im
plemented to query for the current zoom value and update the size and location
of the object accordingly. An AffineTransform for the current zoom is obtained
from the ZoomController and used to transform the component’s shape. The
challenges encountered were updating the ’clickable’ area of the objects and any
arcs connected to them. Mouse clicks did not initially register within the whole
area of the new zoomed components. We discovered that Places and Transitions
had overridden the contains(int x, int y) method of JComponent. This method
defines the area within which the component responds to mouse clicks, and so
had to be altered to reflect the zoomed size. Finally, the objects carefully calcu
late the points at which any arcs attach to them. For a Place this is a point on
27
its circumference. For the Transition we found that an AffineTransform was al
ready being used to calculate the points (to allow for rotation of the transition)
and so we were simply able to concatenate this with our own AffineTransform
to produce the zoomed result.
4.4.2 Arcs
In fact we did not implement zoom in the arcs themselves, but rather in the
ArcPathPoint class. The Arc simply joins up its constituent points to create
itself. In the same way as for PlaceTransitionObjects, a variable was added to
the class to store the ’real’ location of the point, independent of the onscreen,
zoomed position.
4.4.3 Labels
NameLabels are the names of Transition or Place objects, and this information
is stored in the pnName NameLabel variable inherited from PetriNetObject. In
the first iteration of the zoom process, these labels were not updating when the
object was zoomed and moved position. To remedy this, a call to the pnName
variable’s setLocation() method was added to the updateBounds() method of
PlaceTransitionObject, which is called to update the object as part of the zoom.
The new, zoomed coordinates are passed in, along with the height offset of the
object, and the result is that the label is repositioned with the newly zoomed
object.
4.4.4 AnnotionNotes
The AnnotationNote objects inherit from PetriNetObject like everything else,
but because their text still had to be legible, we decided not to change their
size. Therefore the effect of the zoom simply had to be a translation from one
coordinate to another. As with other objects, variables were added to the class
to store the original coordinates of the object, so that these could be used for
saving/loading. The zoomUpdate() method was also added, which gets the cur
rent ZoomController (if not null), calculates the new zoomed coordinates using
the original coordinates of the object, and then uses the original setLocation()
method to repaint the object. This updates all aspects of it, including it’s
draggable points, so no further work was needed.
28
recognises it needs to ’grow’ if it is updated and it’s child components are off
the bottom and/or righthand side of the pane. However, once we decided to
zoom the objects’ distance from the top left corner of the pane, rather than
the centre of the view, it meant that the net was effectively always growing out
to the right and down. The pane can then be resized by calling the existing
method updatePreferredSize(), which iterates over the net objects and sets the
size of the pane accordingly. Originally, PIPE only extended the pane if objects
were created on the very edge of it. We decided it was easier to continue to
draw a net if the pane increased whenever an object was within 100 pixels of
the edge. This way, there’s always plenty of space to scroll to and add the next
object, meaning the application is more usable.
When the JScrollPane enlarges, scrollbars are added and updated automat
ically. However, the viewport remains the same. Therefore, the next challenge
was to reposition the viewport so that it appears to be centred on the same
coordinates as before as detailed in the specification. The scrollbars are cre
ated automatically as children of the JScrollPane, and can be explicitly altered.
We first experimented with moving the scrollbars to certain values in order to
move the viewport and change which section of the net was visible. However, we
soon discovered it was easier to modify the JViewPort component of the pane
directly. We added code to the actionPerformed() method of ZoomAction to
store the coordinates of the top lefthand corner of the viewport prezoom. In
case this was itself a zoomed perspective, we convert these to what they would
be without any zoom. The zoom process then goes ahead, and after the pane
is enlarged if necessary, we calculate the coordinates of the new viewport. The
end result is that the model appears to have been zoomed around the centre
point of the previous view. The scrollbars update automatically.
29
Figure 9: Proposed buttons for zoom
30
Figure 11: Proposed dropdown menu for zoom
31
Figure 12: Final dropdown menu for zoom
32
incremental Zoom In or Zoom Out buttons have been clicked, the name of the
action created will reflect this, and so zoomIn() or zoomOut() can be called
in ZoomController. These methods increment or decrement the controller’s
percent by 10 and use this to set the scale of the controllers Transform variable.
If the name of the action is just Zoom, then it could have been created via
either the dropdown menu or the toolbar selection box. Therefore ZoomAction
checks the source of the action as well, and extracts the text of the selected item
appropriately. However, as the selection box can accept any user input, and the
example percentages themselves contain the ’%’ character, some validation is
necessary before the information can be passed to the ZoomController. If either
of these conditions are violated, then the selection box is blanked out and the
ZoomAction aborted. The validated string is then parsed to an integer and
passed to the ZoomController’s setZoom() method, which modifies the percent
and Transform accordingly.
It was decided that the selection box should update whenever a ZoomAc
tion was completed, regardless of the source of the action. To this end, the
updateZoomCombo() method was created, which is called after the percentage
zoom of ZoomController has been altered and sets the selection box text to this
value. We soon discovered a problem with our original implementation of this,
however; the method to set the selection box text also fires the newly selected
item as well. This meant that each zoom was in fact being actioned twice
highly inefficient! To solve this, we updated updateZoomCombo() so that the
ActionListener on the box is removed before the item is selected, and then added
again afterwards, so no action is fired.
4.5 Evaluation
Overall, we feel that the development of zoom functionality has been a success.
The key objectives outlined in the original specification were as follows:
• when a zoom magnification is selected, each object in the net is redrawn
to the correct scale, and the view centred on the correct co ordinates
• any new objects created with that magnification selected are drawn to the
scale previously selected
• the new buttons are created in the toolbar and clicking them initiates the
correct action. Relevant Alt text should also be added the new dropdown
options are added to the dropdown menu and clicking them initiates the
correct action
All of these objectives have been met. However, the method of implementation
and some smaller details do differ from what was outlined in the spec.
33
PIPE application drew objects. However, the suggestion that certain existing,
unused data members were related to scaling and redrawing object and could
be used as part of a zoom functionality proved to be incorrect. However, this
allowed us to design the implementation from scratch, following MVC concepts
and good objectorientated practices.
The specification did not provide a detailed plan of how the functionality
would be implemented. It specified that the Graphics and Graphics2D classes
of the AWT package would be sufficient to achieve zoom, and this was the case,
although AffineTransform was also needed. However it didn’t mention exactly
what classes or would be created, or whether an interface would be used, so we
simply used its openended instruction that much of the work would concern
the Datalayer and Gui packages as the start point of our research.
34
Figure 13: Representation of the relation between a Petri Net and its Reacha
bility Graph
the analysis side of the application was both an interesting avenue to explore
and potentially quiet rewarding.
The reachability graph was an obvious extension of the work that had al
ready been done on state space analysis and would naturally complement the
information provided on boundedness, safeness and deadlocks. The reachabil
ity graph, a commonly sought after tool when analysing the behaviour of a
stochastic system modelled with Petri Nets, provide a visual representation of
all the Net’s possible firing sequences. The states therefore represented the cur
rent state of the nodes and the transitions between them indicated particular
(single/multi) transitions firing. The example below clearly illustrates the close
relationship between the Net and the Graph, while reinforcing the fact that each
presents and emphasises different aspects of that same body of information.
The graph is a useful tool for finding erroneous behaviour of a system e.g. we
can use it to determine whether using semaphore will prevent the simultaneous
access of the data protected by the semaphore.
If the data is simultaneously accessed by both processes, the above Petri
net will assume a state whose markings = (|start of p1|, |access by p1|, |end
of p1|, |semaphore|, |start of p2|, |access by p2|, |end of p2|) = (0,1,0,0,0,1,0).
However, this state is not present is the reachability graph generated in figure 2,
this proves that using semaphore will allow mutual exclusive access of the data
protected by the data. In addition to this, there are many other uses for the
graph e.g. it can be used to determine whether the net can result in deadlock,
the net is bounded etc.
35
Figure 14: PIPE Petri net representation of a two process semaphore
Figure 15: Reachability graph generated by the above Petri net. Note that S3,
S4, S5, S6, and S7 are in red and represent the vanishing states.
36
5.1.1 Background on Reachability
A Petri net is not static, its markings can evolve dynamically by transition
firings which destroy and create tokens. The reachability graph of a Petri net
is a graph representation of these firing sequences. The graph is directed whose
nodes represent reachability states S and edges represent transitions between
two such states; if there is an edge connecting S1 to S2, it can also be said
that S2 is directly reachable from S1. It is also a multigraph as multiple edges
between nodes are allowed as it is possible for two transitions to be enabled in
the same marking and produce the same state change.
37
5.1.2 Background on generating state space[12]
38
5.2 Research
The research carried out prior to the development of the Reachability Module
was of key importance to both ascertain the requirements of the general users
in terms of desired functionality and simultaneously investigate potential imple
mentation strategies. In terms of the general user, the needs were fairly basic
and easily pinpointed due to feasibility constraints and actual beneficial gains
provided by the extension itself. The user requirements of the module were
therefore as follows:
• Graphical representation of the reachability graph via an additional mod
ule.
• Inclusion of information within the graph in terms of both state numbers
and transition explanations.
• Limitation of reachability graph search space in terms of number of ex
plored states, hence enforcing an upper limit constraint on graph’s beyond
which both viability and actual additional information obtainable from the
graph become minimal.
In contrast with the user requirements, the potential implementation strategies
were both numerous and extremely varied in approach. The key disputable
areas were as follows:
• The manner in which we obtained the state space information.
• The methods through which we transformed this data into its graphical
representation (this was going to be a flat bitmap graph according to the
specification).
Initial research focused on new algorithms that would perform the analysis on
the Petri net and generate its corresponding reachability graph data. However,
it became obvious that completely overhauling the algorithms implemented in
the GSPN Analysis module would both be cumbersome and provide only min
imal gains in terms of efficiency. Additionally, the objective of this part of the
project was not to rework other modules but rather increase the overall func
tionality of the software package. Therefore we pushed this design choice in
favour of manipulating implemented algorithms in order to obtain the required
output. The second stage of the implementation research was highly centred on
technologies that would enable us to represent data in a graphical format. In
particular, the challenge was to find open source viable solutions to the problem
of automating the graph drawing process.
5.2.1 Technologies
The need for a graph visualisation package was immediately apparent from the
outset of this module’s development. The research focused on graph visualiza
tion techniques which enable structural information to be represented as dia
grams of abstract graphs and networks. More importantly though this process
39
Figure 16: Example of Graphviz engine generating automated graph layout
needed to include automated graph drawing. This would tackle the fundamental
problem of obtaining a graph layout algorithm capable of determining optimum
positioning of the elements of the graph.
5.2.1.3 Dot File Format The dot file format provided the necessary data
structure that would be needed to translate reachability graph information into
40
its graphical representation. Grappa was implemented using this format for a
number of logical reasons. The coding format of dot files is both user orientated
in terms of readability at its source level and also enables quite detailed con
trol over the manner in which Graphviz automates the layout algorithm. The
format allows users to specify numerous fundamental properties of the graph in
question, for example:
• Node shape, colour and positioning (if the user requires fixed nodes)
• Graph orientation in terms of direction top to bottom, left to right, etc.
• Detailed labelling both within nodes and between them labels can be
fixed, can change visibility state depending on node selection or inter node
selection, etc.
The format therefore seemed ideal for the level of control we required over
the actual structure of the resulting graph. Also, this format enables users
to significantly customize graphs and thus could help future developers extend
the functionality of the module through higher levels of user input in terms of
desired graphical layouts. Once the dot file is constructed, it is then parsed
by Graphviz to construct hierarchical or layered drawings of directed graphs.
When dealing with the dot file format, the Graphviz layout algorithm directs
edges in the same direction (top to bottom, or left to right) and then attempts
to avoid edge crossings and reduce edge length.
5.3 Design
Having pinpointed the relevant technology that would be used to implement the
new reachability module, a number of design decision then needed to be taken
into consideration.
41
in its original form, we decided that hosting the application as a remote ser
vice for the PIPE application would be an acceptable compromise. By hosting
Graphviz as a service, the PIPE application would remain platform independent
yet would now include an additional constraint as it requires internet connec
tivity. Taking into consideration the fact this introduced only minor limitations
on the application (given the extent of today’s internet access) we decided to
push forward.
5.4 Implementation
5.4.1 Steady State Algorithm
The principle of the algorithm[12] used is pretty straightforward. It is a deep
first search and will terminate only when the state space is finite. First, the
initial marking (M0) of the net is calculated and it is added to the state space.
The transition enabling rule described in lemma 2 is then used to generate an
array of enabled transitions and the firing rule described in lemma 3 is used to
compute the set of states reachable from M0. In this way, new states can be
iteratively added to the state space by selecting a state that is already present
in the state space, generating its reachable states and then inserting them to the
state space. In order to generate the smallest set of possible states as defined
in lemma 1, it is necessary to keep a list of explored states so that a state that
is reachable by more than one state is only added once to the state space.
As the state space of the net increases exponentially with its size, special
considerations have to be taken in terms of the memory requirements and com
42
Figure 17: Class diagram outlining of some of the structure and interactions of
the Reachability module
1. A primary hash key h1 (s) is calculated to determine the row of the hash
43
Figure 18: UMLSequence diagram for generating a reachability graph from a
Petri net.
44
Figure 19: Taken from p17 of Nadeem’s report summarising the hashing
technique used to store states in the explored
state list
table
2. A secondary hash key is calculated to get the data stored in the table,
which contains only compressed state information, which is simply an
integer value, and not the full state description
Each row of the hash table contains a linked list whose elements are the single
integer compressed state information.
As seen in the diagram above, the combined h1 and h2 key can be used as
an identifier for a particular state and thus this information can be used instead
of full the state description to check whether a particular state has already been
added to the state space. However, the combined h1 and h2 is not unique for
every state description; it is possible to generate the same hash key for two
completely different states. This disadvantage is offset by the 25fold decrease
in the memory required to store each state.
In order for the graph to distinguish between vanishing and tangible states,
the following rule is used to correctly categorise the states in the state space.
At a particular state S,
45
To generate a directed reachability graph, a list of states reachable by the
current state needs to be recorded. This is achieved by the use of the arcListEle
ment object created by Nadeem to store rate information between two states.
An extra attribute is added so that it can hold transitions information, namely
the transition number responsible for the change of state observed. Once this
information has been recorded, it can be taken out of the list, thus a stack is
chosen over a normal list to store this information together with this list of
reachable states that haven’t been added to the arcListElement.
All the above information is output into two separate records, the state
record and the transition record. Markings of the state and the Boolean isTan
gible attribute are stored in the state record. The connections between the states
and the fired transition number are stored in the transition record. Knowing
how the state space can explode exponentially with the complexity of the net,
a random access file is used to output this information despite this sacrificing a
little computational speed, as accessing information from a random access file
will be slower than accessing information from a sequential access file. The main
reason for using a random access file is the fact that it enables you to write the
information anywhere in the file. By creating an offset for the location where
the transition records are stored in the file, it is possible to store both the state
record and transition record simultaneously as soon as they have been calcu
lated. This reduces the memory requirement of the output operation by half,
because if a normal sequential access file is used, a complete list of state records
and transition records has to be created before writing to the file; otherwise it
will not be possible to retrieve the information orderly later on.
46
Figure 20: Java IO class hierarchy (http://Java.sun.com/)
47
through which the user could interact with the reachability graph. These options
include but are not limited to deleting states, zooming, saving and acquiring
additional information when hovering over states or transitions.
5.5 Evaluation
The following three criteria are examined to test the correctness of the reacha
bility graph, as opposed to its modelling accuracy:
5.5.1 Quality
Two main factors are being considered here the GUI and humancomputer
interaction (HCI). The interface should be aesthetically pleasing and blend in
with the rest of the modules. It should also be intuitive (userfriendly) and
functionally relevant. Visualising the graph should assist the users with un
derstanding the state space of the net. Appropriate colour coding, labels and
informative legends should be used to aid the use and visualisation of the graph.
A dialog box was chosen as the interface to display information and get
input from users because it conforms to the appearance of the other modules.
The main drawback of this dialog box is its inflexibility as a toplevel container
and its modality, which effectively blocks all interaction between other windows
unless it has been closed. This created a lot of problems for us in getting the
graph JFrame window to interact with the user without having to close the
module window. After testing several methods, we managed to get the graph
window to pop up on top of the module window and allow the users to interact
with the graph window before having to first close the module windows. This
makes the module more userfriendly as users can now generate multiple graphs
from a single module window and they do not have to close the module window
first before they can interact with the graph window.
5.5.3 Robustness
The module should be able to handle invalid file inputs. A limit should be set
for the maximum number of nodes on the graph:
48
1. To ensure that the graph can be generated within a reasonable amount of
time
2. To prevent an average user from running into memory problems due to
the explosion of the state space
3. To ensure the quality of the graph it would be pointless to create the
graph if it is going to be a mesh and users cannot intuitively interpret it
The module should always reliably produce the same graph for any two identical
nets. Any exceptions should be handled gracefully.
The main tool used in accessing the robustness and correctness of the module
is the test suite developed during its implementation. Details of the tests can
be found below.
5.5.3.2 Unit Testing Unit testing was employed during the implementa
tion of the module to validate the correctness of each function. These tests
helped to ensure correct output and graceful handling of exceptions and in
valid/incomplete parameters inputs.
It is essential to check that the correct state space is generated and the right
result is passed onto the dot file generation function, otherwise it will lead to
the generation of an inaccurate reachability graph. The integrity of the state
space generated is checked as follows in the StateSpaceGeneratorTest:
1. Check that the state space generated by our algorithm have equal or more
states than the set of tangible states generated by Nadeem’s algorithm[14]
as the set of tangible states is a subset of the set of reachability states.
2. If the state space contains only tangible states, the state space generated
should be equivalent to the set of tangible states generated by Nadeem’s
algorithm[14].
3. Check for uniqueness of the states in the state space as the state space
generated should contain the least number of states as defined in equation
1, thus there should be no replication of states in the state space.
49
The state space data is exported out via a random access file. State informa
tion and transition information are represented as state records and transition
records in the file. Therefore, to check that the right information is passed
from the state space generation function to the dot file generation function, it
is important to access the integrity of the functions in the state record and
transition record class. These functions are tested in the StateRecordTest and
TransitionRecordTest, which literally tests whether the original data is modified
in anyway after it has been written and read on the other end.
Having verified the integrity of the state space generator, it was then neces
sary to begin testing on the actual reachability module. This part of the testing
required verification of the dot file generator algorithm as well as the grappa
port. Two fundamental authentication steps were necessary to achieve this:
1. Verify whether the data received from the state space generator was cor
rectly translated into the dot file format carried out by the reachability
GraphGeneratorTest.
2. Check the actual reachability graph for correct number of states and tran
sitions following the dot file parsing and subsequent graph generation
through the grappa engine.
Having checked the major functionality of the module, one final test (ModuleM
anagerTest) was implemented to verify the module was correctly loaded via the
module manager. This naturally overlapped with tests that had been written
to asses the refactoring of the module loading classes and were therefore used
for dual testing.
Having verified the integrity of the state space generator, it was then neces
sary to begin testing on the actual reachability module. This part of the testing
required verification of the dot file generator algorithm as well as the grappa
port. Two fundamental authentication steps were necessary to achieve this:
1. Verify whether the data received from the state space generator was cor
rectly translated into the dot file format carried out by the reachability
GraphGeneratorTest.
2. Check the actual reachability graph for correct number of states and tran
sitions following the dot file parsing and subsequent graph generation
through the grappa engine.
Having checked the major functionality of the module, one final test (ModuleM
anagerTest) was implemented to verify the module was correctly loaded via the
module manager. This naturally overlapped with tests that had been written
to asses the refactoring of the module loading classes and were therefore used
for dual testing.
After the module had been implemented, we tested it rigorously. We are able
to demonstrate that we are able to reproduce the same graph with fidelity for
each example nets. During the testing phase, we also found that the maximum
50
number of nodes the graph can represent without obscuring its visualisation
and interpretation to be around 400, hence, we have set that as the limit for
the module.
5.5.4 Efficiency
Generation of a graph with less than 100 nodes should be moderately fast but
it is acceptable for graphs with greater than 1000 nodes to be generated less
quickly.
On average, we have determined that it takes less than a second to generate
graph with less than 15 nodes and about 10 seconds to generate a graph with
100 nodes. Out of the 10 seconds, the steady state generation only took ap
proximately 0.1 second; most of the time is spent on transmitting the dot file to
the remote server at http://www.research.att.com/~john/cgibin/formatgraph
and waiting for its response. If speed is an issue, users can always download a
copy of Graphviz and invoke the remote call locally. This will reduce the time
taken for graph generation by 10fold.
5.6 Improvements
As in all software development projects, a number of possible improvements
might have been made both to the method of solving the problem and the
manner in which the method was implemented. Hindsight naturally makes
some of the short falls more apparent although given the time constraints of the
project we feel that the objectives of this module have been achieved.
Nevertheless, there are some improvements that we would like to suggest.
In our view the core change that could be made to this module is the imple
mentation of Graphviz hosting at Imperial College. This would provide long
term stability for the module and remove our reliability on the hosting of a nec
essary service via a third party (AT&T). Nevertheless, this was placed on the
list of future recommendations for two reasons. First, this problem is simple
to solve given the necessary time and not fundamental to the module in the
medium term, and therefore was not seen as a critical area to leave as a future
improvement. Secondly, during the course of the project we were able to con
tact the individual running the service at AT&T and received confirmation that
for the foreseeable future the service would remain available thus providing a
minimum guarantee towards the future proofing of the application.
6 Testing
A testing framework was implemented using the junit and Abbot open source
libraries as outlined in the specification document.
51
Figure 21: UML class diagram of Pipe test framework
52
is made possible by the junit.extensions.abbot.ScriptFixture class, which is the
basis for test scripts written in xml. We derived pipe.gui.FunctionalScriptsTest
from ScriptFixture and generated a series of scripts to test the main GUIlevel
functionality of PIPE as thoroughly as we could. The xml test scripts are gen
erated with the help of a script editing tool that is provided with the Abbot
package. This tool generates xml references to GUI components that allow
the Abbot classes to identify them correctly, and records user actions directly
from the application, so that they can be written into the script and played
back. These action steps are then interleaved with assertions which generate
exceptions if they are broken, following the same pattern as the other tests.
53
some covering the analysis modules that were worked on by the team, and others
testing some of the GUI classes in a more methodspecific way than the scripted
tests.
54
Part III
EVALUATION AND
CONCLUSIONS
55
7 Evaluation and learnings
The group is satisfied that in most cases the tasks it set out to do have been
achieved beyond the level specified.
Refactoring of the application was to some extent continued as we worked on
adding new functionality and fixing existing bugs, so was somewhat more thor
ough than initially expected. Having said that though, the group felt that
progress with the refactoring was at times frustrating due to a lack of familiar
ity with the code, with Java and with working on collaboratively on a software
project. It is generally agreed that the refactoring might have gone further had
it been done towards the end of development, but of course this could have
sacrificed the quality of new functionality that was added to PIPE.
The original specification for reachability graph functionality included the gen
eration of flat bitmap images from a supplied DOTformatted; however, by
incorporating the Grappa code it was possible to offer highly interactive graphs
to the users, including zoom in and out, scaletofit and tool tip (additional in
formation displayed on mouse hover) functionality. Users can reposition nodes
and specify layout prioirities.
The group is very happy with the quality of the zoom functionality: the feature
of being able to drag Nets smoothly across the page is particularly effective, and
something which wan’t specified initially. It was however chosen as a substitute
for the initially proposed fit to page functionality, so was a case of adapting
the specification to better suit perceived users’ needs rather than simply adding
extra features to the specification.
One task which wasn’t completed was the updating of options functionality,
which had been specified in the original document and scheduled for the final
week of the project. We had seen this as a ’nicetohave’ feature and in the end
the team chose to ensure the main new pieces of functionality were effective and
thoroughly tested rather than take something on and run the risk of incorporat
ing a feature we wouldn’t have had time to test. This is certainly functionality
we would recommend for future generations of PIPE developers.
For most of the team, this was the first time they had taken on an existing live
development project. This led us to slightly underestimate the time required
to gain a good understanding of the application. The presence of bugs and
unwieldy code was a legacy of previous contributors. Becoming familiar with
PIPE’s code base and its idiosyncratic and in places suboptimal design was at
times frustrating but was undoubtedly a useful experience, and of course some
thing professional developers working on largescale projects face on a regular
basis. We therefore consider this to have been a valuable experience, and will
make appropriate allowances for becoming familiar with future coding projects.
56
8 Effectiveness of Scheduling and Group Organ
isation
The Gantt charts on the following pages show the scheduling and status of tasks
at the outset, midpoint and end of the project. Task completion fitted remark
ably closely with the planned timings. For the two major pieces of functionality
the group split into two teams, and we had agreed that if necessary, resources
could be moved from one task to the other. However, this proved unnecessary,
so we stuck to the original plan.
The group had taken time at the outset to understand each other’s areas of
expertise and interests; task allocation was to some extent influenced by these
and we feel that this led to each member working as effectively as possible.
Emphasising facetoface communication over detailed written documenta
tion meant that we had flexibility and could respond rapidly to unexpected
developments. Nonetheless, the initial schedule was successfully adhered and
it would certainly be wrong to suggest that there was any lack of discipline or
organisation as a result of our approach.
9 Future Directions
There are many potential improvements that could still be made to PIPE. We
would hope that future development continues to be guided by users. Some
possible areas are listed below.
57
58
References
[1] http://pipe2.sourceforge.net/documents/PIPE2 Final Report.pdf
[2] http://pipe2.sourceforge.net/documents/PIPE2Report20050814.pdf
[3] http://pipe2.sourceforge.net/documents/PIPEReport.pdf
[4] Dijkstra, E.W.: Hierarchical Ordering of Sequential Processes. Acta Infor
matica 1 (1971) 115138
[5] http://sourceforge.net/projects/pipe2/
[6] http://Java.sun.com/docs/books/tutorial/uiSwing/layout/index.html
[7] http://Java.sun.com/docs/books/tutorial/2d/advanced/transforming.html
[8] http://www.cs.umd.edu/hcil/jazz/index.shtml
[9] http://www.famfamfam.com
[10] http://www.eclipse.org/tptp/
[11] http://www128.ibm.com/developerworks/Java/library/jcq03316/
61
[12] M. Ajome Marsan et. al. (1995). Modelling with Generalized Stochastic
Petri Nets (John Wiley and Sons, 1995, West Sussex)
[13] W.J. Knottenbelt Generalised Markovian Analysis of Timed Transition
Systems MSc thesis, University of Cape Town, June 1996
[14] http://pipe2.sourceforge.net/documents/PIPE2Report20050814.pdf p14
19 [last accessed 15/3/07]
[15] http://ostermiller.org/utils/CircularBuffer.html
62