Porting Visual Basic Applications to Java: A View from the Front Lines
WE'VE ACHIEVED A benchmark; a data point in Java's progress toward becoming a fully-functional platform for serious application development. We completed a Java 1.1 port of a 27,000-line network management application written in Visual Basic 5.0 (VB). Where possible, code and functionality were matched line-for-line and feature-for-feature. This is not meant to be a comprehensive review of the differences between Java and VB, but a practical guide to the similarities and differences that became important to us during the port. We hope that our experience will benefit anyone considering a transition from the Windows-tied VB platform to the platform-independent Java platform.
Platform-independence was, after all, the main reason for porting our application to Java. VB was initially chosen because of its reputation for rapid development, and it served its purpose. However, our company makes network devices that must operate in shops running Windows, various flavors of UNIX, and even MacOS. By writing our network management application in Java, we can support all these platforms with one codebase. This goal has been referred to as the Holy Grail of application development.
Our application allows the network administrator to manage a set of networking hubs and switches. It provides Graphical User Interface (GUI) visibility to basic monitoring (temperature, fan operation, which ports are active, what is connected to those ports, etc.) as well as control over some sophisticated protocol-level diagnostics that are built into our hardware.
The application has three main architectural layers (Figure 1): managementAPI, managed objects, and the GUI. The managementAPI is a monolithic object that allows specific pieces of information to be get( ) from and set( ) on a network device. It encapsulates the underlying communication with the devices, which, in this case, is done with Simple Network Management Protocol (SNMP) running on top of UDP on top of IP. The managed objects and collections thereof, are the model of the network so far as the GUI is concerned. There are Agents, Hubs, Switches, and Ports all under an object called the Site. Through regular and aptly timed polling, these objects keep their states synchronized with the states of the physical entities they model. The GUI views are the user's window on and interface to these states. The views keep themselves synchronized with the managed objects by registering as listeners to events the managed objects generate whenever their state changes. This use of the Observer Pattern1 was chosen in preparation for making a client/server cut between the GUI and the managed objects in a later release. For now, all three layers operate under the same Java Virtual Machine (JVM).
Figure 1. Three main architectural layers.
We explore some important differences between the Java and VB platforms, and how we decided to port around these differences. We'll review the challenges we faced in porting the GUI portion of the application. We then move on to examine the potentially profound impacts of moving from a single-threaded runtime (VB) to a runtime that is inherently multi-threaded (Java).
Language Differences and Their Impacts
Blasted Variants. One of the most pervasive problems our team had to deal with was the use of Variants in the VB code. Variants are untyped variables that can be anything from strings to pointers to arrays. Worse, Variants can change the type of data they represent as the program runs. While marvelously convenient for VB programmers, trying to port code that uses Variants to Java, which is much more strongly typed, is difficult. Typing provides clues about the quantities variables represent. It tells you which operators and comparisons are applicable to a given variable. Using Variants removed these clues and resulted in many questions posed to the original author of the VB application along the lines of "what is this thing?"
Globals. Our VB app consisted of class files (.cls), basic files (.bas), and form files (.frm, .frx). Variables defined in basic files can have global scope and need not be associated with a given class, as in Java. Globals are generally frowned on in object-oriented (OO) circles, but our mandate to "port the code as closely as possible" dictated their use. We ported these variables by creating a class called Globals and filling it with public static Java variables (see Listing 1).
Enums. Through its global scoping, VB supports enumerated types (variables that take on one of a set of integer values) that are especially useful in switch-case constructs. We implemented enums in Java in a more OO way that worked out most beneficially. We encapsulated each enumerated type in its own class, which was derived from an EnumBase class that handles most of the mechanics.
EnumBase (see Listing 2) implements many of the methods for getting and setting values, and handles bounds checking to make sure that the value can not be set out of the allotted range. The BASE_VALUEs were assigned so that none of the Enums had ranges that overlapped. This setup provided a good measure of type and validity safety over using pure ints, albeit by mechanisms that were active at runtime rather than compile time.
Data Hiding
Within class definitions, the mapping of visibility modifiers (e.g., public and private) was quite adequate, because Java has even more shades of visibility (protected, package) than VB.
Optional Arguments: Overloading or Null Fields? A feature VB has that Java does not directly support (a good thing in our opinion) is optional arguments to member functions. In VB, function arguments may be declared Optional in the function definition. The code inside the function can detect which arguments were passed into it using VB's IsMissing() function. This resulted in two porting strategies, both of which were used in the final product. The obvious OO way of dealing with this was to overload the method. This was done in cases where the additional processing that had to be done with the extra arguments could be easily separated from the basic part of the method. This way, the overloaded form of the method could chain-call the basic part of the method and achieve code reuse. This could not always be done, however, when the Optional arguments were woven into the logic of the basic part of the method. In these cases, we opted for a second strategy: a single method declared with all the parameters, some of which (the Optional ones) could be null. The code that checked for the Optional parameters using IsMissing() in the VB code was simply changed to check for a null value in Java (see Listing 3).
Passing Parameters By Reference and By Value. Another difference in function calls between VB and Java is that VB passes everything by reference unless the ByVal keyword is specified. Java passes primitive types (int, boolean, etc.) by value and passes all objects by reference. The only way to achieve a pass-by-reference of a primitive would be to wrap the primitive in an object. (Note, java.lang.Strings are immutable objects, so they are effectively passed by value.) To port a pass of an object by value, you can pass it by reference and immediately clone() it (assuming it is Cloneable) before you use it inside the method.
This mismatch between the languages, particularly with Java's multi-threading capabilities (see below) could have resulted in some nasty bugs. Fortunately, good programming techniques used by the original author (she did not use two-way parameters) minimized the possibility of bugs (see Listing 4).
Language Differences Require Fastidious Coding. It was interesting to observe the differences in coding styles that could be supported by VB and Java. Weakly typed languages such as VB and Perl tend to support hacky coding if the programmer is of a mind to do so (e.g., you can do a less-than comparison between a character and an integer in VB if you want to). These techniques are much harder to use in a more strongly typed language such as Java.
GUI Down vs. Architecture Up. Another difference between the VB and Java styles is the method by which the programmer builds the application. In VB it is not uncommon to start by dragging and dropping GUI components into dialog boxes and then filling in functional code behind each widget. OO Java programmers would be much more likely to design from the Model up to the GUI View, the opposite approach.
No Inheritance in VB. Along the lines of OO design, a major difference is that VB is not, technically, an OO language2 because it does not support inheritance. Effectively creating class hierarchies during our port gave us a significant amount of reuse that was not available to the original author.
VB Collections, Java Vectors, and Hashtables
Beyond simple arrays, both VB and Java support more sophisticated one-dimensional collections of objects that can grow and shrink dynamically. In VB, these are called Collections. Elements in VB Collections can be iterated through without an index, numerically indexed, or indexed according to string "keys." Sometimes we ported these collections as java.util.Vectors and sometimes as java.util.Hashtables. It depended on whether the key indexes were used or not. Cases where the elements were indexed both numerically and by key in VB were problematic, and new code had to be written to make up for Java's lack of schizophrenia in this area (see Listing 5).
Challenges in Porting the GUI
Porting the VB application's GUI to Java required a slightly different approach from that of the other modules in the system. Because of differences in the event model and the lack of inheritance among objects, it was deemed that while the GUIs must look the same, the underlying architecture would be allowed to take advantage of Java's OO features.
The VB application that we ported was structured as a "single document interface" (SDI) application. It had a main frame (window) to which components were dynamically added and removed as the application ran. Clicking on the contents of this window or choosing menu items from its menu bar created other frames that could be positioned independently of the main frame.
Choosing Swing
The components within the frames were mostly native VB components such as text, labels, and buttons, but also included some custom-made components such as simulated Light Emitting Diodes (LED). We decided to use Sun's Swing 1.0.3 package of the Java Foundation Classes (JFC) for the Java app's GUI because of the broad array of components available. We also decided that any additional components would need to be Swing-based so as to avoid the thorny issue of the interaction between lightweight Swing and heavyweight java.awt components. Some thought was given to making the components JavaBeans, but we decided that there were not enough good reasons or time to justify the (admittedly minor) additional work.
As previously mentioned, the VB app's GUI was created using the graphic design tools of the VB development environment (i.e., by dragging-and-dropping components into the frame), and resulted in code that contained hard-coded font styles and sizes and absolute values for the location and size of the components. From the beginning of the porting process, it was mandated that the Java app should exactly duplicate the VB app in both look and feel. (This was despite the fact that it was then targeted for a platform other than Microsoft Windows.) This made it impossible to use graphical design tools to create the Java GUI, as the resulting layout could not be tweaked to mimic that of the VB app. (We could have used an absolute-position Layout Manager for our views, and possibly been able to use the design tools, but we agreed that hard-coding the layout was too limiting for future plans.) The GUI was therefore constructed "by hand," making use of a dizzying assortment of nested JPanels and Boxes, with help from most of the Layout Managers, to duplicate the layout of the VB app. This type of "trial and error" development was time-consuming, error-prone, and resulted in code that is not readily reused in other instances.
The Event Models
Java's event model (1.1 and later) is much richer than that of VB, so porting the behavior of the GUI was simple. What had been a frightening number of event-handling callback routines in the VB app was organized by classes and collapsed via inheritance and multiple instantiations.
One very welcome benefit of the Java GUI components over their VB cousins was that Java did much of the work that originally had to be handled explicitly in VB. For example, changing the imagery of the mouse cursor requires entry and exit methods for each VB component. The methods must set and reset the cursor imagery as appropriate. This error-prone and imperfect paradigm compares poorly to that of Java, in which you simply assign an image to a component and lets the component manage the details.
Look and Feel
We decided to allow the Java app to use the look and feel of the platform it was running on. Subsequent to the release of our port, Sun published guidelines for the look and feel of Java applications.3
An Insurmountable Obstacle
One shortcoming of Java and Swing (recently fixed in Swing 1.1.1) constantly eluded a solution during the porting. The different frames in the VB app each used a different icon in the frame titlebar to indicate at a glance what the frame's purpose was. This was particularly useful when frames were minimized to the "speedbar."
The "frameicon" (as it is called in Java) of a JFrame cannot be changed.4 (We could have used Swing's JDesktopPane and JInternalFrame, but that would have required taking on a "multiple document interface" paradigm, which was not in keeping with the VB app.)
GUI Threading Issues
Because the Java app, and therefore the GUI, was multi-threaded (more details follow), special precautions had to be taken to ensure that the state of the visuals was consistent with the underlying data. In particular, because Swing is not thread-safe,4,5 some of our GUI code made use of Swing's invokeAndWait() or invokeLater() methods. In cases where this was inappropriate, other methods of synchronization were used. This was all in stark contrast to the VB app, which didn't need to concern itself with threading issues.
Single vs. Multi-Threading
A profound difference between VB and Java applications is that VB applications are single-threaded and Java applications are usually multi-threaded. The introduction of concurrently running code can introduce innumerable complications. The mindset with which you write multi-threaded code is very different than when you know there will be only one active thread conducting atomic actions. For example, you have to worry about how the code will handle a shared resource such as a file when two threads want to open and write to it at the same time.
According to Doug Lea,6 the issues raised by multi-threading lie somewhere between the goals of thread-safety and liveness. Thread safety ensures that there are no undesirable effects caused by multiple threads operating simultaneously to change the application's state. Liveness concerns arise when synchronization methods are put in place to ensure thread safety but cause the application to behave sluggishly or freeze. Particularly dangerous is a deadlock in which two or more threads go into a holding pattern waiting for a condition that, exactly because both threads are holding the resource that the other wants, will never develop. In this case, the application hangs and must be restarted-not the kind of behavior you want customers to see.
When porting code line for line, you do not have the luxury of designing and coding for concurrency. There are some class and object methods that can and should obviously be synchronized, but how do you know that this will not lead to a deadlock? If you apply or do not apply these controls to ensure thread safety, what will the consequences be? These were not questions we felt we could answer at the beginning of the port, and they weighed heavily on our minds as work proceeded. Time constraints dictated a wait-and-see approach.
In the Java port of our application, there is a main thread that starts the application running, a GUI thread that processes user input, a polling thread that periodically sends out SNMP requests to update our managed objects, and multiple threads that originate in the managementAPI layer each time an answer to an SNMP request comes back asynchronously from an SNMP Agent. It is typical for 10-20 threads to be active at any given time.
As we began to run with this system, we discovered that it was necessary to throttle the SNMP threads so that only one thread could enter the managed objects at a time, to avoid rather frequent deadlocks due to partial synchronization. We also installed "First-In-First-Out" event queues between the managed objects and the GUI to throttle event processing somewhat and to ensure that threads on the server side of the application would not block for GUI updating. Each GUI listener uses an active event queue that acts only when new events have been deposited by other threads. When we were done with these modifications, we still had a multi-threaded app, but one with some controls in place to provide thread safety and avoid deadlock.
Is our app thread-safe? In all honesty, probably not. Still, the funny thing about thread safety and liveness problems is that 9,999 times out of 10,000 your code runs just fine. Depending on your application, this may be acceptable. In our case, by creating a forgiving architecture, which relied on redundant updating of the state of the GUI, we were comfortable with the small risk. The Swing library is not designed to be thread-safe, yet we conducted tests with multiple threads banging on the same Swing components at once and convinced ourselves that the lack of safety was acceptable. Similarly in our app, we have not observed misbehavior that is obviously due to a lack of thread safety, and we would not ship a product that would hang due to thread deadlock. We were able to provide enough synchronization and throttling to meet our needs. It is not as desirable as if the system had been designed for concurrency from the beginning, but it seems to work well enough to meet our requirements. As professional programmers, it is important for us to remember that, much as we may dislike it, business requirements sometimes take precedence over our desire to achieve code Nirvana.
References
1. Gamma, E. et al., Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995.
2. Booch, G. Object-Oriented Analysis and Design, Addison-Wesley, 1994.
3. Sun Microsystems, Java Look and Feel Guidelines, java.sun.com/products/jlf.
4. Walrath, K. and M. Campione, The Java Tutorial, Sun's Java Series, java.sun.com/docs/books/tutorial/uiswing/overview/threads.html.
5. Muller, H. and K. Walrath, Threads and Swing, Sun's Swing Connection, 1998, java.sun.com/products/jfc/tsc/archive/tech_topics_arch/threads/threads.html.
6. Lea, D., Concurrent Programming in Java, Addison-Wesley, 1997.