When I recently embarked on Java development for some telecommunications clients, I quickly learned that pure Java code is not a PR fad, it is a technical necessity. A few short months after Sun announced the 100% Pure Java initiative, I was grappling with development tools and third-party code that was itself in transition. Here are some tips and comments based upon my real-world experience at producing and verifying pure Java code.
The rationale for committing to Java in the first place was very clear. I worked for a firm that was growing nearly 100 percent each year as it developed both a professional services organization (PSO) and a practice specializing in call centers solutions (CSC). The PSO, among other things, targeted building Web-enabled client/server applications that linked databases to the Internet and corporate intranet.
I was refining the client software for use by telecommunications firms, as well as the middleware infrastructure for tying together legacy applications with distributed desktops. Telecommunications is a highly competitive market where customer care and customer service are increasingly important criteria for differentiating one firm from another. I was providing client software with four foundation modules - subscriber, agent, sales management and administration - plus many options for customization and custom add-ons. Telecom firms typically have a lot of downstream legacy systems and databases that have to be integrated into these client modules, so engineers were working with Tuxedo transaction monitors, Oracle databases and CORBA-based messaging.
In such an environment, I had to deliver a very thin client application that was platform-independent. Users would be distributed throughout both corporate intranets and to subscribers on the Internet. There was simply no way to know whether users had PCs, Macs, workstations or network computers. Pure Java code was a technical necessity.
That said, the 100% Pure Java initiative was only a few months old. Some tool vendors and many suppliers of third-party modules were still getting up to speed on ensuring the code they generated or delivered was indeed Pure Java. Engineers worked through code glitches and, in some cases, assisted third-party partners in making sure their code was fully compliant.
Corporate developers and development partners should not pigeon-hole the Pure Java initiative as merely an independent branding program for software vendors. Java Pure Check, which is freely available in conjunction with the initiative, is an essential tool for in-house testing. And as the certification program continues to evolve, it is becoming far easier for internal development teams to insist that third-party code modules they bring into their firm are truly 100% Pure Java code.
Even if there is no intention to verify the purity of Java code with an independent testing lab, using Java Pure Check should be part of any good quality assurance process. Testing all classes and methods is simply an excellent requirement, especially as hectic development schedules stretch resources and create a temptation to look for short cuts, or as development teams expand. My experience confirms that the Pure Check process provides an excellent quality assurance building block that is very consistent with ISO certification requirements that methods be in place to guarantee all software is tested before deployment.
It is also important to recognize that the inherent capabilities of Java, JavaBeans and Enterprise JavaBeans make possible a fundamentally superior testing methodology for corporate developers and large integrators. Developers can not only separate server and client applications more distinctly than ever before, they can also separate out business functionality from enterprise plumbing issues on the server side.
Testing procedures can, therefore, focus not so much on testing applications as they do on testing components. Shifting the focus to component testing enables a team to verify functional chunks and should dramatically boost productivity.
Clearly separating application components also makes it far easier for development teams to roll out new functionality. Complex enterprise applications will include Java and non-Java elements. Java-based functionality can be added incrementally over time, as well as upgraded, over time. Adhering to well-defined interfaces is crucial to leveraging that flexibility.
For my clients, there were also strong business drivers behind the commitment to Java in general and to the specific commitment to ensuring the code met Pure Java standards. Java is platform-independent, it is secure, it is ubiquitous and it solves the client software distribution problem for network-centric applications. Ensuring that Java is always Java - that it is 100% Pure - is essential to delivering the goods. It is also these inherent qualities that make possible thin-clients, including network computers.
Whether on a large intranet or an extranet where business partners and customers access a firm's computing resources, companies want to minimize release management headaches. From the users' perspective, whether the application works properly will shape the perception of the company's reliability, so regardless of the client platform, the software needs to run well. For telecommunication firms that means there are some pretty expensive ramifications to inconveniencing customers or business partners that go well beyond the well-documented impact of superior customer service on customer loyalty.
Pure Java Requirements
Java Pure Check focuses on the purity of Java code rather than the portability of program functions. Purity is a far more objective measure and Pure Java programs substantially reduce the risk of portability problems now and in the future.
Sun defines a Pure Java program as one that relies only on the documented and specified Java platform. It is, therefore, a self-contained set of classes with no external dependencies other than the Java Core API. A Java program may be an application, an applet, a class library, a servlet, a JavaBeans component or more than one of the above. For formal certification, if an application uses libraries outside the Java Core API, those libraries need to be packaged with the application and must be 100% Pure Java as well.
Purity is measured at the bottom edge of the program where it interfaces with the platform, rather than at the top edge where it interfaces with the user. As such, purity is a good predictor of portability and a pure program should not be accidentally unportable. Programs can be checked for purity individually, so client and server side Java programs can be evaluated in a completely independent manner.
As the above information indicates, there are some hard and fast rules about writing Pure Java applications. The first is that there can be no native methods in the application.
Introducing native code into a Java program sacrifices most of the benefits people look to Java to deliver -- security, platform independence, garbage collection and easy class loading over the network. The security implications for users can be very significant, wiping out assurances that the code is free of viruses and making it far more likely that memory problems -- pointer overrun or attempting to access protected memory -- can crash the Java Virtual Machine.
That said, native method definition and some methods in the java.lang.Runtime class do provide access to hardware-specific code, which is useful for Java programs that access legacy systems. By definition, however, the interface programs cannot be Pure Java.
Where it seems necessary to include native methods to deliver access to a system resource that is not properly supported by Java, or to boost performance, there are a couple of alternatives. One is to define a simple protocol to give that service and then write a Pure Java program as a client of that protocol. Another is to rewrite the native method in Java.
The Java Native Method Interface (JNI) does not make native code platform-independent, although it does make it easier to port native code. The native code still must be recompiled for each different hardware and that recompilation will be difficult or impossible if the target hardware does not provide the library or the capabilities required by the native method.
A second rule is that applications must depend only on the Java Core APIs and should not depend upon the internals of any particular Java implementation. Standard classes and standard interfaces are crucial for Java to be Java. The Java Core APIs provide the basic language, utility, I/O, network, GUI and applet services.
There are certain methods in the Core API that must be called or implemented in a certain pattern. By failing to follow these patterns it is possible to write a program that is syntactically correct, but which will be highly nonportable. Sun-s Pure Java Cookbook provides five or so examples and Java Pure Check will catch most of them.
On the flip side of the coin, any implementation of the Java Core APIs will include classes and packages that are not part of the documented API interface. Portable programs must not depend on these implementation details as they may vary between different Java implementations. Sun cautions that this is true even if the classes in question are undocumented parts of its reference Java platform implementation. Those interfaces are not part of the Java platform definition and they are not checked by the tests for Java compatibility so they may be absent or may behave in subtly and dangerously different ways on different Java implementations. They are not documented because they are not intended for client use.
One subtle way that a program may depend on implementation details is by defining classes into the packages that are part of the Core APIs or a specific implementation. This breaks protection boundaries that the Core implementors are entitled to count on. Another subtle dependency on implementation details is direct use of the AWT component peer interfaces defined in classes in the java.awt.peer package. These interfaces are documented as being "for use by AWT implementors". A portable program will use the AWT rather than implement it.
Hardwired, platform-specific constants are a definite portability problem and a violation of Pure Java guidelines. Hard-coded file names should not be used and directory paths tied to a file name are even worse. Similarly, input and output streams can be used unportably with hard-coded and hardware-specific line termination characters. Java Core APIs do provide portable alternatives that should be used.
The most portable way to construct a File for a file in a directory is to use the File(File,String) constructor to build up the path. Other portable solutions are to use the system properties to get the local file separator and starting directory, or to use a file dialog to ask the user for a filename.
Various hardware platforms may also have a variety of environmental differences, such as screen size, available fonts or different color pallets. Developers need to look for the most portable way to implement functions that are affected by such variables. Don-t hard code text sizes, for instance, and have applications get the font names from the java.awt.Toolkit.getFontList method rather than using a hardwired font list.
Applications vs. Applets
Most developers would agree that writing portable applets is more challenging than writing portable applications. Applets extend the java.applet.Applet class. Portability, however, can be affected by a variety of other factors, including the Web page the applet loads from, the other classes the applet uses, the HTML that loads the apple, and the security manager and AppletContext of the user-s browser.
For example, the HTML <applet> tag requires that the applet markup follow certain rules. <param> elements must come before the alternate contents. Alternate contents are text elements, like the contents of a paragraph - the <p> tag. This means no <p> tags can be included in the alternate contents.
Applets will almost certainly have to run under the control of a security manager. There is, however, no standard profile for security managers. The user can instruct their security manager to deny any combination of access. The best answer is for developers to make sure that an applet handles any security exception gracefully.
Similarly, the Java standard does not specify required content types or protocols. The MIME types image/jpeg and image/gif should be safe, as are the http:, file: and ftp: protocols. Developers should again ensure that the applet will handle any errors gracefully.
And finally, the Java 1.0 API specification of the AppletContext/Applet protocol was not very precise. The result of this ambiguity is that different browsers call the applet-s enter and leave methods at different times. The protocol specification is much more precise in the Java 1.1 API, so this problem will disappear in time. Until Java 1.1 is universally deployed, developers should ensure that an applet that behaves politely on one browser does not hog resources on another, or that an applet that functions normally on one browser does not stall on another.
Developers charged with creating Pure Java applications should certainly read Sun-s guidelines and its cookbook for creating portable Java applications. There are also various online discussions of bugs and other factors that sometimes make universally portable Java code more challenging than developers desire. In addition to Java Pure Check, I also use the rest of the Java quality assurance tools developed by Sun-s SunTest business unit. These include Java-Star for GUI testing and JavaSpec for API testing.
Development teams must, in addition to employing good testing tools, clarify their expectation for Pure Java coding and make sure all team members understand what that means. With the proper foundation, an emphasis on delivering Pure Java code wherever possible can offer substantial benefits to the developer.
About the Author
Bob Adams is Director of Business Development for Cupertino, California-based SoftPlus.He can be reached at [email protected]