Although Java became popular as the programming language of the Web, it has evolved beyond that to become a language for developing a wide range of software. For example, you can use Java to write applets, window and console applications, beans, servlets, and distributed objects. Don't worry if you're not familiar with these terms, because you'll learn about them in this chapter. You'll also learn how to move your legacy C and C++ code to Java. When you finish this chapter, you'll have a better understanding of which types of software you can develop with Java and how to convert your existing software to Java-based applications.
Java is most closely associated with applets. An applet is a Java program that is executed in the context of a Web page. It's loaded and executed by any Java-capable Web browser that displays a Web page referencing the applet.
Applets are referenced in Web pages using the Hypertext Markup Language (HTML) <APPLET> tag. Chapter 5, "JDK 1.2 Applet Writing Basics," shows how to use this tag to include applets in Web pages.
Applets consist of compiled Java code that is stored on a Web server, along with the Web pages from which they are referenced. The applet code can be stored in the same directory as the Web pages or in separate directories that are used to store only applets.
Applets can be used in a variety of ways. They can be used to create fancy Web page widgets, such as animated advertisements, drop-down menus, or advanced forms. They can also be used to implement complex Web-based applications, such as database front-ends, Web-based training systems, and terminal emulators, and to bring entertainment to the Web by providing custom multimedia players and, of course, a wide range of games. The major advantage of applets is that they transform passive Web pages into programs that interact with Web users.
Applets are developed by creating subclasses of the Applet class of the java.applet package. You create special methods that support applet initialization, the starting and stopping of an applet, and applet termination. You also provide GUI controls in your applets and methods for handling GUI events. The Applet class is covered in Chapter 5 in the section "The Applet Class."
The Java execution environments of Web browsers and the tools of the JDK provide a high level of security, aimed at protecting Web users from malicious applets. As you learned in Chapter 3, "The Extended Java Security Model," the security mechanisms provided by these execution environments are modeled after a sandbox--applets are permitted unrestricted access within the sandbox, but are blocked from making any accesses outside the sandbox. The extended security model, introduced with JDK 1.2, provides a more flexible approach to permitting trusted applet access outside the sandbox.
If your main interest in Java software development is applet programming, you're in luck. Part II of this book, "Applet Programming," begins with the next chapter.
Most of the programs that we run on a day-to-day basis are standalone programs that we execute on our PCs. With the advent of the Macintosh and Microsoft Windows, most of these programs are window-based. Java is a great language for developing these window-based programs. The JDK provides an extensive API for window program development. The Abstract Window Toolkit (AWT) of the java.awt packages allows window programs to be developed in a platform-independent manner. Once you develop a window program in Java, you can run it on Microsoft Windows (98, 95, NT, and CE), Macintosh, UNIX, OS/2, and other windowing environments. This is an incredible capability, considering the difficulty involved in porting non-Java window programs from one windowing environment to another.
In addition to the powerful windowing capabilities provided by the AWT, JavaSoft has introduced the Swing API with JDK 1.2. This API provides a broad range of advanced GUI controls, ranging from spinners to multilevel list boxes. It also provides the capability to easily tailor the look and feel of the overall GUI presented to the user. You'll learn how to develop Java window applications in Part III, "Application Programming." You'll learn how to use the Swing API in Part IV, "Swing Programming."
NOTE: The AWT, Swing, Java 2D API, Accessibility API, and Drag and Drop API are all part of the Java Foundations Classes (JFC) 1.1, which is integrated with the Core API of JDK 1.2.
In addition to being used to write attractive window programs, Java can also be used to write console programs. These programs read data entered by the user at the keyboard and write data to the user's console window. If you're an old-timer, you probably remember using console programs on MS-DOS or UNIX systems. Java console programs don't have to be simple. You can develop a range of sophisticated applications as console programs. For example, in Chapter 32, "Server Programs," you'll develop a multithreaded Web server as a console program. You'll learn the basics of how to write console programs in Chapter 10, "Writing Console Applications."
Since the advent of structured programming, the goal of software engineering has been the development of software components that can be reused in a variety of software applications. This goal was partially achieved by the last generation of component development tools pioneered by Microsoft's Visual Basic and Borland's Delphi. These tools supported the development of Component Object Model (COM) software components that could be reused in Microsoft Windows programs. JavaBeans picks up where these tools left off and provides the capability to develop platform-independent software components that can be reused in applets, standalone applications, and server programs.
JavaBeans are Java-based software components that are designed for maximum reuse. They are intended for use in visual software development environments. You use visual development tools to construct JavaBeans. Once a bean has been developed, you use the drag-and-drop capabilities of visual tools to add beans to your applets and applications and tailor them to your particular needs.
Beans are often visible GUI components, but they can also be invisible algorithmic components. You can use beans for developing user programs such as applets and applications. However, you can also use beans in server programs and in distributed applications. JavaSoft provides a bean bridge so that beans can even be used in legacy COM-based applications, such as Microsoft Word and Excel.
Beans are self-contained software components that easily can be tailored and added to a wide range of software applications. Since the introduction of JavaBeans, thousands of Java beans have been developed, hundreds of which are available as off-the-shelf software components. If you are interested in writing Java-based software components, be sure to read Part VII, "Creating JavaBeans."
Most of us are familiar with applets, standalone applications, and GUI-based software components because they're designed for end users. We use these types of programs on a daily basis to perform our work, interact with the world, or entertain ourselves. Another category of software that we may be less familiar with is server software. Server programs run behind the scenes to provide vital data and services for our user applications. Examples of server programs are Web servers, mail servers, file servers, database servers, and so on. Server programming is covered in Chapter 32, "Server Programs."
In addition to these large server programs, there is a need for small customized server programs that perform specialized tasks. For example, the Common Gateway Interface (CGI) programs are executed by Web servers to perform Web searches, process form data, and provide dynamic feedback to Web users. CGI programs are designed to be small, fast, and efficient, and are written to support specialized Web applications.
Recognizing the need for this type of server programming, JavaSoft has developed the Java Server Toolkit and the Servlet API. The Java Server Toolkit is a client-server framework for building Internet and intranet servers. It implements the functions that are common to many servers, such as listening for client connections, processing client connections in a multithreaded manner, and servicing requests made by clients over these connections.
The Servlet API is used to develop custom server-side programs for the processing of client requests. It can be used as a Java API for writing CGI programs, and is supported by all major Web server products. However, the Servlet API extends beyond CGI programming. It is envisioned that servers of all types (not just Web servers) will support the use of servlets for servicing client requests.
You'll learn about server programming in Java in Part VIII, "Network Programming," and in Part XI, "Server-Side Java."
Now that we live in a networked world, the natural course of software development is to create applications that are distributed over a network. The client-server computing of applets and Web servers is an example of a distributed application. However, future distributed applications will consist of many objects that are distributed over several computers.
For example, a financial application running on your personal computer may invoke methods of objects that run on another computer belonging to your company's intranet. These objects may search company databases for the information used by your financial application, process that data according to your company's business rules, and make that data available to your financial application. The results calculated by your financial application may be automatically forwarded to an information distribution object, which will make the results available to other employees within your company as well as selected vendors and customers. Figure 4.1 depicts this example distributed application.
FIGURE 4.1. An example of a distributed application.
JavaSoft provides a Java-based approach to developing distributed objects that can be used to build distributed applications. This approach is referred to as remote method invocation (RMI). RMI allows Java objects executing on one host to invoke the methods of objects that execute on remote hosts--hence the name remote method invocation. The remotely invoked objects perform services and may return values that are used by the local objects.
The Remote Method Invocation (RMI) API of Java is a Java-specific approach to developing distributed systems. RMI's major advantage is that it is fully integrated with the Java object model, highly intuitive, and easy to use. In addition to the RMI API, the JDK provides a number of tools for building distributed systems.
What RMI means for you as a software developer is that you can build distributed applications with remote Java objects that are purchased off-the-shelf or custom-developed by others. You can also develop distributed objects that can be used by others. Part IX, "Developing Distributed Applications," covers the use of RMI in developing remotely accessible objects and integrating these objects into distributed applications.
RMI is a 100% Java solution for developing distributed applications. If you have the flexibility to develop your distributed applications entirely (or almost entirely) in Java, RMI is the way to go. However, large enterprise applications tend to be heterogeneous, and sometimes Java alone is not enough. In these cases, a mixed-language distributed object development approach, such as that supported by the Common Object Request Broker Architecture (CORBA) is a better solution to distributed application development.
CORBA's strong point is that it supports a language-independent model. It provides a standard architecture for developing distributed object-oriented systems. This architecture specifies how a client object written in one language can invoke the methods of a remote server object developed in a different language. Figure 4.2 provides an overview of this architecture.
FIGURE 4.2. Using CORBA to build distributed applications.
CORBA makes use of objects that are accessible via Object Request Brokers (ORBs). ORBs are used to connect objects to one another across a network. An object on one computer (client object) invokes the methods of an object on another computer (server object) via an ORB.
The client's interface to the ORB is a stub that is written in the Interface Definition Language (IDL). The stub is a local proxy for a remote object. The IDL provides a programming-language independent mechanism for describing the methods of an object.
The ORB's interface to the server is through an IDL skeleton. The skeleton provides the ORB with a language-independent mechanism for accessing the remote object.
Remote method invocation under CORBA takes place as follows. The client object invokes the methods of the IDL stub corresponding to a remote object. The IDL stub communicates the method invocations to the ORB. The ORB invokes the corresponding methods of the IDL skeleton. The IDL skeleton invokes the methods of the remote server object implementation. The server object returns the result of the method invocation via the IDL skeleton, which passes the result back to the ORB. The ORB passes the result back to the IDL stub, and the IDL stub returns the result to the client object.
JDK 1.2 provides support for interfacing Java objects with CORBA objects. This support is provided through Java IDL (an ORB), the idltojava compiler (used to generate stubs and skeletons), and tnameserv, an object naming service.
The capability to interface with CORBA objects means that Java can be used to develop industrial-strength enterprise objects for use in large, heterogeneous distributed applications. This capability opens yet another door for the Java programmer. Chapter 41, "Java IDL and ORBs," shows how to use Java IDL to interface Java objects with CORBA objects. If distributed application development is of interest to you, also check out Chapter 54, "Dirty Java," where you'll learn how Java can be used to support Microsoft's approach to distributed application development (known as the Distributed Component Object Model or DCOM).
The preceding sections covered some of the broad areas of Java software development. Within each of these areas, there are numerous specialty areas for Java software development. Part V, "Enhancing Your Applets and Applications," shows how to use the capabilities provided by the Java API to add features such as drag-and-drop and internationalization to your programs. Part VI, "Multimedia Programming," shows how to take advantage of the new multimedia features supported by JDK 1.2. Part X, "Database Programming," shows how to use JDBC to access online databases from your applets, applications, and server programs. The rest of this chapter provides suggestions on how to move your existing legacy software, written in C and C++, to Java.
MOVING C/C++ LEGACY CODE TO JAVA
Java is a powerful language that provides many useful features to the software developer. However, if your software organization is typical of most, you will have to trade off moving to Java with the constraints imposed by a dependency on in-place legacy code. This section summarizes the pros and cons of moving existing legacy code to Java. It identifies a spectrum of approaches for accomplishing software transition and discusses the issues involved with each approach. It also covers approaches to translating C and C++ code to Java. This section assumes that the transition of C/C++ code to Java is being performed by a moderately large software organization. Some of the software porting issues become insignificant if only a few small programs are translated into Java.
When you're deciding whether to move existing applications to Java, you must consider the trade-off between the advantages and disadvantages of such a move. This section identifies many of the advantages of Java programs over C-based and C++-based applications. The following section considers some disadvantages of using Java and identifies roadblocks to any software-transition effort.
One of the most compelling reasons to move to Java is its platform independence. Java runs on most major hardware and software platforms, including Windows 98, 95, and NT, Macintosh, and several varieties of UNIX. Java applets are supported by Java- compatible browsers, such as Netscape Navigator and Internet Explorer. By moving existing software to Java, you can make it instantly compatible with these software platforms. Your programs become more portable, and any hardware and operating-system dependencies are removed.
Although C and C++ are supported on all platforms that support Java, these languages are not supported in a platform-independent manner. C and C++ applications that are implemented on one operating system platform are usually severely intertwined with the native windowing system and OS-specific networking capabilities. Moving between OS platforms requires recompilation, at a minimum, and significant redesign in most cases.
Java is a true object-oriented language. It does not merely provide the capability to implement object-oriented principles; it enforces those principles. You can develop object-oriented programs in C++, but you are not required to do so; you can use C++ to write C programs as well. Java does not allow you to slip outside the object-oriented framework. You either adhere to Java's object-oriented development approach or you do not program in Java.
Java is one of the first programming languages to consider security as part of its design. The Java language, compiler, interpreter, and runtime environment were each developed with security in mind. The compiler, interpreter, API, and Java-compatible browsers all contain several levels of security measures that are designed to reduce the risk of security compromise, loss of data and program integrity, and damage to system users. Considering the enormous security problems associated with executing potentially untrusted code in a secure manner and across multiple execution environments, Java's security measures are far ahead of even those developed to secure military systems. C and C++ do not have any intrinsic security capabilities. Can you download an arbitrary untrusted C or C++ program and execute it in a secure manner?
Security and reliability go hand in hand. Security measures cannot be implemented with any degree of assurance without a reliable framework for program execution. Java provides multiple levels of reliability measures, beginning with the Java language itself. Many of the features of C and C++ that are detrimental to program reliability, such as pointers and automatic type conversion, are avoided in Java. The Java compiler provides several levels of additional checks to identify type mismatches and other inconsistencies. The Java runtime system duplicates many of the checks performed by the compiler, and performs additional checks to verify that the executable bytecodes form a valid Java program.
The Java language was designed to be a simple language to learn, building on the syntax and many of the features of C++. However, in order to promote security, reliability, and simplicity, Java has left out those elements of C and C++ that contribute to errors and program complexity. In addition, Java provides automated garbage collection, freeing you from having to manage memory deallocation in your programs. The end result of Java's focus on simplicity is that it is easy to learn how to write Java programs if you have programmed in C or C++. Java programs are also less complex than C and C++ programs, due to the fact that many of the language elements that lead to program complexity have been removed.
The Java language provides many language features that make it preferable to C or C++ for modern software development. On the top of this list is Java's intrinsic support for multithreading, which is lacking in both C and C++. Other features are its exception-handling capabilities, which were recently introduced into C++, its strict adherence to class and object-oriented software development, and its automated garbage-collection support. In addition to these features, Java enforces a common programming style by removing the capability to slip outside of the class- and object-oriented programming paradigm to develop C-style function-oriented programs.
Although C and C++ have been standardized by the American National Standards Institute (ANSI), many C and C++ compilers provide custom enhancements to the language, usually through additional preprocessor directives. These enhancements usually make their way into source code programs, resulting in a general lack of standardization. Java does not yet suffer from any standardization problems because its syntax and semantics are controlled by a single organization.
The predefined classes of the Java API provide a comprehensive, platform-independent foundation for program development. These classes provide the capability to develop window and network programs that execute on a wide range of hosts. The Java API's support of remote method invocation, database connectivity, and security are unmatched by the API of any other language. In addition, no other language provides as much platform-independent power as Java's API.
Sun has taken important steps to support fully distributed computing with its support of RMI, CORBA, and JDBC. These APIs provide the capability to develop and integrate remote objects into standalone programs and applet-based Web applications.
Because Java is an interpreted language, it can be used to rapidly prototype applications that would require considerably more base software support in languages such as C or C++. The Java API also contributes to the capability to support rapid code generation. The classes of the Java API provide an integrated, easy-to-use repository for the development of application-specific software. Because the Java API provides high-level windows, networking, and database support, custom application prototypes can be constructed more quickly using these classes as a foundation.
Java software is essentially self-documenting when doc comments and the javadoc tool are used to generate software documentation. The excellent Java API documentation is an example of the superior documentation capabilities provided by Java. Because Java software is inherently better structured and documented than C or C++ software, it is generally easier to maintain. In addition, the package orientation of Java software provides considerable modularity in software design, development, documentation, and maintenance.
Java's many benefits make it an attractive language for developing new applications and porting existing legacy code. The previous section discussed some of the advantages of porting existing code to Java. This section identifies some of the disadvantages of any migration from C or C++ to Java.
Although Java is supported on many platforms, it is not supported on all of them. If your target hardware or software platform does not support Java, you are out of luck. Your alternatives are to switch to a different platform or to wait for Java to be ported to your existing software platform.
Also, your operating system or browser platform may not support the latest version of Java. For example, Netscape Communicator 4.0 supports JDK 1.1, but Microsoft Internet Explorer supports most of JDK 1.1 but not all of it. Earlier browsers support JDK 1.02. In order to develop Java software that is compatible with a wide range of users, you must ensure that your users are upgraded to an execution platform that runs the version of Java required by your software.
Compatibility may also be a problem at the design level. Suppose that your target software platform does support the latest version of Java. If your legacy code is unstructured and incompatible with a class- and object-oriented model, the effort required to migrate the software may be prohibitive.
Java is interpreted, and although its execution is efficient, it might not meet the performance demands of applications in which execution speed is of paramount importance. Examples of these types of applications include numerical "number crunching" programs, real-time control processes, language compilers, and modeling and simulation software.
Just because your application fits into one of these categories does not necessarily rule out Java. For example, the Java compiler is written in Java and performs admirably for small programs. However, its performance is greatly enhanced when it is compiled into native machine code instructions. Java-to-C translators allow programs to be developed in Java and translated into C for native machine code compilation. (See http:// www.cern.ch/WebOffice/Projects/Newspaper/tools/toba/doc/ for an example.) The translation process generally improves the performance of Java programs. Some Java development tools, such as Symantec Visual Café for Java Professional, provide the capability to create native binary code executable files (.exe) directly from Java code.
Probably the biggest boost to Java's performance is the HotSpot technology from JavaSoft. HotSpot allows Java programs to execute as fast as or faster than compiled programs. HotSpot increases execution performance by integrating a just-in-time compiler and a code optimizer with the Java interpreter.
Retraining
Although Java is simple, easy to learn, and based on C++, some training will be required to get programmers up and running with it. This is especially true if the programmers haven't been using C++ in a structured, object-oriented fashion. I never really appreciated the object-oriented programming features provided by C++ before I began programming in Java. Until I had adopted the Java program-development mindset, I was trying to apply my outdated and inefficient C++ programming techniques to Java software development. After I had made the mental transition to the Java object-oriented programming model, I became much more comfortable and efficient in writing Java programs.
Moving legacy code to Java may result in adverse effects on company operations that are supported with legacy software. This is especially true when the legacy code is implemented in the poorly structured, convoluted manner that typically evolves from extensive software patches and upgrades. In the case when existing system software is tightly coupled and fragile, a transition to Java (or any other language) may break the software application to the point where a complete software redevelopment is required.
Any software transition effort is subject to cost and schedule constraints. Moving current legacy software to Java might not be cost-effective, given the current software investment and its expected operational life. The software transition may also have a significant impact on system availability and prior scheduled activities. Transition from C or C++ to Java might also require a significant level of effort that would exceed the expected budget for the maintenance of the legacy code.
There are many ways to integrate Java into existing software applications. This section identifies some of these approaches and explores the issues involved in making the transition to a Java-based software environment.
One of the easiest ways to introduce Java to an operational environment is to use it to add functionality to existing legacy code. Java programs do not replace existing legacy software; they merely enhance it to support new applications. This approach involves minimal impact to existing software, but introduces a potentially thorny maintenance issue because Java is added to the current list of languages that must be used to maintain the system.
You can reimplement legacy code in Java in increments, moving over to a Java-based software-development approach while minimizing the impact on existing legacy software. This approach assumes that the legacy software is developed in a modular fashion and can be replaced in an incremental manner. If this is the case, legacy software can be migrated to Java on a module-by-module basis, with the legacy code ultimately replaced by new Java software.
If in-place legacy code can be upgraded using Java software that is implemented on separate hardware platforms, Java can be used to off-board many of the functions performed by the legacy code. The use of off-board server software allows the investment in legacy code to be preserved, while expanding the services provided by the system as a whole.
In some cases, it is more cost-effective to keep legacy code in place while completely redeveloping system software from scratch. This is typically the case when the system is subject to large-scale reengineering, or when it is so fragile that it breaks as the result of the simplest upgrades. If full-scale system redevelopment is necessary, this is actually an advantage to Java software development because the developed software is under no legacy-compatibility constraints and can take full advantage of Java's capabilities.
Translation of existing C and C++ code into Java can be performed in several different ways, depending upon the compatibility of the existing software with Java. This section describes some of the different approaches to software translation.
Tools and utilities have been developed that allow Java source code and bytecode to be translated into C to support native machine code compilation. Future Java integrated software-development environments are planned, where either Java or C++ code may be generated based on the configuration of the development software. These development tools will allow easy movement between C++ and Java and require a common set of libraries that can be used by either Java or C++ programs. Automated translation between these two languages will be supported to some extent.
The degree to which C++ programs may be automatically translated into Java will depend on the planning and effort put into the code's design. Factors to be considered include compatible libraries, the use of single inheritance, the use of object-oriented programming capabilities, and the minimization of the use of incompatible language features.
Manual translation of C and C++ to Java will probably be the most common approach to moving C and C++ legacy programs to Java. This approach requires you to use two editor windows--one for the legacy C++ code being translated, and the other for the Java program being created. Some of the translation is accomplished by cutting and pasting C++ statements into the Java window, making the corrections necessary to adjust for language differences. Other parts of the translation require that new Java classes, interfaces, variables, and methods be developed to implement C++ functions and data structures that cannot be directly translated from C++ to Java. The effectiveness of the manual translation process will be determined by the degree to which the C++ legacy code meets the compatibility considerations identified at the end of the previous section.
In many cases, manual translation is hampered because the C++ legacy code is written in a style that renders it impossible to migrate using cut-and-paste translation methods. In these cases, a class- and object-oriented design of the legacy code needs to be extracted from the legacy code and used as the basis for the Java source code development. A two-level approach to software translation is followed. The legacy code is reverse-engineered to an object-oriented design, and the recovered design information is used to develop a Java software design, which is in turn translated into Java source code. Code is not translated from one language to another. Instead, legacy code is translated into general design information that is used to drive the Java design and implementation.
In this chapter you've explored the broad range of Java software development. You learned how Java is used to develop applets, console and window applications, beans, servlets, and objects for distributed systems. You also learned how to move your legacy C and C++ code to Java. In the next chapter you focus on applets and learn how they're used to bring interactive content to the Web.
© Copyright 1998, Macmillan Publishing. All rights reserved.