Level: Introductory David Carew (carew@us.ibm.com), Senior e-Business Architect, IBM Jeff Wilson (wilsonje@us.ibm.com), e-Business Architect, IBM
27 Feb 2004 This is the final installment of Getting on the open road, a three-part series of roadmaps designed to assist developers of .NET, Windows client/server, and ASP applications make the jump to the Java platform. In Getting on the open road, the authors help you leverage your existing development knowledge to ease the way onto the path of open-standards-based programming. In this article, veteran enterprise architects David Carew and Jeff Wilson provide a high-level guide for ASP developers who want to migrate their e-business applications to J2EE or build J2EE applications from the ground up. Share your thoughts on these articles with the authors and other readers in the accompanying discussion forum. (You can also click Discuss at the top or bottom of the article to access the forum.) Are you an ASP developer looking to port an existing e-business application to J2EE or writing a new J2EE application from scratch but don't want to suffer from "information overload" to get started? Then this roadmap is for you. This roadmap is specifically designed for ASP developers looking to make the jump to J2EE, a platform independent set of open standards for developing Web applications and other types of distributed applications in the Java language. Along the way we'll introduce you to J2EE, programming in the Java language, developing J2EE Web applications, and relate it to what you already know from your experience writing ASP, COM objects, and ISAPI filters for IIS and the Windows environment.
Why J2EE?
If you're not exactly eager to take the J2EE plunge, consider these J2EE benefits:
- Choices, choices: Because J2EE is a well-defined set of standards, you have a choice of J2EE implementations when you deploy your code. As long as you stick with the standard APIs and avoid any vendor-specific extensions, your application will run on a variety of implementations without coding changes.
- Did we say choices?: J2EE implementations are available on a variety of platforms, from mainframes to Wintel, UNIX, and Linux. You can write your application once and deploy it on a variety of platforms.
- Can't we all just get along?: J2EE includes a standard API for accessing many legacy EIS systems like CICS, IMS, ERP, and CRM systems. It also includes support for Web services, so you can integrate with .NET systems and other systems supporting industry Web services standards. J2EE also includes support for a standard messaging API (Java Message Service; JMS) and an API for access to relational databases (Java Database Connectivity; JDBC). This wide array of choices lets you integrate with a variety of existing systems without losing your investment in them.
- No smoke-filled back rooms: Experts from around the world develop J2EE specifications through the Java Community Process (JCP). The JCP releases initial drafts of specifications for public review. Even if you don't actively participate, you always know what's in store for future specs. Specifications also include a reference implementation you can use to check out new technology before you decide to implement it.
 |
Introducing J2EE
Java 2 Enterprise Edition, is a specification made up of many component specifications related to developing distributed applications in the Java language. You can use J2EE components to write Web-based applications and traditional client-server applications, and to connect to legacy resources like relational databases using a standard API. If you're coming from an IIS/ASP development background, Java Servlets and JavaServer Pages (JSP) technology are the components of the most interest to you.
 |
Migrate to the Java platform
If you develop .NET, Windows client/server, and ASP applications, and are contemplating making the leap to the J2EE platform, our series Getting on the open road will guide you through the process, leveraging your existing development knowledge to ease the way onto the path of open-standards-based programming.
|
|
Java Servlets
Java Servlets are Java classes that run as an extension of a Web server like IIS or the Apache Web server. A Java Servlet is analogous to an ISAPI filter or a cgi-bin program/script. A servlet runs when a client browser invokes a specifically configured URL directly or indirectly. A servlet has access to all the information in an HTTP request and can handle the request directly by providing content to be returned to the client. Alternatively, a servlet can redirect the client browser to another resource. Most J2EE Web applications use servlets primarily as targets of HTML forms, to handle user input and then process it accordingly. Generation of the response page is typically delegated to a JSP page.
JavaServer Pages technology
JSP pages are analogous to ASP pages. That is, they are HTML pages that also contain scripting elements that execute on the server when a users requests the page. A key difference between ASP pages and JSP pages is that ASP uses VBScript or JScript as its scripting language, whereas JSP pages use the Java language. A typical JSP page contains snippets of Java code and some special HTML-like tags defined in the JSP specification, interleaved with standard HTML to provide a combination of static and dynamic content. The difference between the Java Servlets and JavaServer Pages technologies is conceptually similar to the difference between difference between an ISAPI filter and an ASP page. In both cases the former is a piece of code you can use to emit HTML directly or redirect to other resources, and the latter is an HTML document that can contain embedded code.
Web servers and application servers
As an ASP developer, you know that ASP pages are executed by a scripting engine that IIS invokes. You also can add ISAPI filters and COM components to your Web applications for IIS to invoke. This makes it easy to deploy Web applications on IIS. But it limits you to the Windows platform, the only one where IIS can run. J2EE uses a different approach because it's designed to run on a variety of operating systems (including Windows). Rather than attempt to embed the code to run Java Servlets and JSP pages directly into a Web server, it uses a separate server component called an application server to run them. Most application servers (such as IBM WebSphere) also have separate plug-in components to bridge between the application server and specific Web servers. For example, WebSphere ships with separate plug-ins for IIS and the Apache Web server. This lets you use the Web server of your choice when you run J2EE components.
The application server's role as a separate, pluggable server component gives you several advantages:
- Choice of Web servers: You're not tied to any one Web server to serve up your static HTML pages. You can continue to use your favorite Web server for this purpose and use any application server to handle your Java Servlets and JSP pages. This capability is especially useful when you're porting an ASP application to J2EE. You can continue to run IIS and port your application in stages. You needn't rewrite the whole application in one fell swoop.
- Choice of platform: You can write your J2EE application once and deploy it on a variety of operating systems -- including Windows, AIX, and Linux -- where the application server can run. You're not limited by where a particular Web server can run.
- Choice of application server vendors: Because industry-standard specifications define the Java Servlets and JavaServer Pages technologies, you can write your J2EE application once and then deploy it to multiple application-server environments, such as WebSphere Express or Apache Tomcat, a popular open source application server. J2EE also defines how you must package a Web application, so you can take an application you develop in one J2EE environment and redeploy it to another application server without needing to change your code or recompile the application. The same holds true for deploying an application to multiple platforms.
How an application server runs servlets and JSP code
As we mentioned earlier, the J2EE specification mandates a standard format for deploying Java Servlets and other J2EE components. An XML document called a deployment descriptor is part of this standard format. The deployment descriptor contains a mapping from each of your servlets to the URLs you use to invoke a particular servlet. An application server uses the information in the deployment descriptor to decide which servlet to invoke for a given request.
The way an application server invokes a JSP page is different from the way ASP pages are invoked. A J2EE application server converts each JSP page into a separate special servlet that compiles and runs when the page is requested. The special servlet remains loaded in memory until the JSP file changes. This minimizes the performance hit that might result if you had to create and compile a class for each JSP page.
Model-View-Controller architecture
J2EE was developed with a particular application structure in mind, called Model-View-Controller (MVC). MVC defines a clear separation among three tiers of an application:
- The model: The set of data and business rules for the application -- commonly called the application's business logic
- The view: The application's user interface
- The controller: Definition of the way the application reacts to user input or to changes in the model tier -- commonly called application logic
Advantages of a MVC architecture
Nothing in J2EE forces you to structure your applications with a MVC architecture, but there are good reasons to do so. By defining a clear separation among the three tiers, MVC allows for loose coupling among the components that comprise each tier. This makes the components more reusable and flexible. For example, suppose one of your requirements is to support different types of views of the same data in a Web application, because different departments need different subsets of the same data in a database. You need to develop new view components specific to each required subset. If the view logic and database-access code were tightly coupled -- as they are an ASP page that interleaves database-access code with HTML -- then each view would contain the database-access code. Maintaining duplicate code not only takes a lot of work, but it's likely to lead to errors. A MVC architecture in this scenario uses the database-access code as part of the model, and the various view components can reuse it.
J2EE components and MVC
Figure 1 shows how the J2EE components we've discussed so far could map to a MVC architecture. Note that no connection exists between the model and the view. The controller's function is to serve as a conduit between the two.
Figure 1. MVC with J2EE Web applications
In a typical scenario, the user submits an HTML form whose target is a servlet. The servlet parses the input and uses classes in the model to invoke the business logic to satisfy the request. The servlet then passes the results to a JSP page to display them to the user.
JavaServer Faces
The JavaServer Faces (JSF) specification provides the runtime components that let J2EE tool vendors provide drag-and-drop capability for the development of Web-based UIs. It also lets vendors develop custom components their development tools can use. To see JSF in action, take a look at the WebSphere Studio version 5.1.1 family of tools (see Resources). WebSphere Studio has a fully integrated tool called Page Designer that you can use to develop HTML pages and JSP pages visually with drag and drop. Page Designer has implemented the JavaServer Faces specification, and allows you to drag and drop components like HTML form elements on to your pages as well as more advanced components that allow you to bind an HTML table to a back-end data source.
Other J2EE technologies
Java Servlets and JSP technology give you the tools you need to build platform-independent Web applications in the Java language. Some other J2EE specifications and components give you more-advanced capabilities:
- Enterprise JavaBeans (EJB) technology: Enterprise components (or beans) that come in three varieties:
- Session beans: Special Java classes that are analogous to COM objects running under the control of the Microsoft Transaction Server or MTS objects. Like MTS objects, session beans run in a container -- the MTS Executive in the case of MTS objects and the EJB container in the case of session beans. EJB containers provide services such as declarative transaction management, role-based security, seamless access in a distributed environment, and activation on an as-needed basis. Session beans come in two flavors:
- Stateless: No state is maintained between method calls, so you must provide all the information needed to invoke a method via parameters. The advantage is that the container can use any instance to service any client invocation.
- Stateful: State is maintained between method calls so clients are always associated with a particular instance. The advantage is that clients can use a conversational mode to interact with stateful session beans. This is especially useful when the intermediate state information is costly to re-create.
- Entity beans: Special Java classes that are object representations of persistent data stored in a relational database or other persistent stores. They can encapsulate the tables in a database schema and can also encapsulate relationships between tables in the data model. Like session beans, they run in a container that provides services such as declarative transaction management, role-based security, and seamless access in a distributed environment. Entity beans are shared objects, so the container also handles concurrency control and ensures that the underlying persistent data maintains its ACID (Atomicity, Consistency, Isolation, and Durability) properties. (Unlike session beans, entity beans are shared objects, so multiple clients can access individual instances concurrently.) In a nutshell, entity beans shield you from direct access to the underlying persistent store. You can deploy them to a variety of persistent stores without any application changes. (That is, you can map an entity bean to its persistent store at deployment time without any code changes.) So, for example, you can remap a set of entity beans that are mapped to an Oracle or SQL Server database to DB2 without any application changes.
- Message-driven beans: Special Java classes that serve as listeners for JMS-compliant messaging middleware. JMS is a standard Java API for accessing message queues. You can configure message-driven beans to point at a particular message queue; the container activates them when a message arrives in that queue. With message-driven beans, you provide the application logic that's invoked when the message arrives. Each J2EE 1.3 compliant application server must provide a JMS implementation, but you can also use popular messaging middleware like WebSphere MQ (formerly MQSeries).
- Java Connector Architecture (JCA): A standard API for accessing legacy EIS systems like CICS, IMS, ERP, and CRM systems. JCA frees you from having to learn a separate API for each EIS system.
 |
Java programming fundamentals
Before diving in to some J2EE programming concepts, we'll introduce you to the Java programming language. You can use the Java language to write server-side applications as well as desktop applications with GUIs. This article assumes you want to use the Java language on the server side to complement a Web-based interface, so we'll skip the GUI programming environment and focus instead on the platform's nonvisual aspects. We'll start with an introduction to the Java Software Development Kit (SDK) and then show you how to write the time-honored Hello World application in Java code. Then, we'll explore the differences between Visual Basic 6 and the Java language. If you're a C/C++ programmer, you can skip this section and take advantage of the tutorial "Java programming for C/C++ developers" instead (see Resources).
Introducing the Java SDK
The Java SDK is a group of command-line tools and packages you need in order to write and run Java programs. Java programs are compiled into platform-independent bytecode by a Just In Time (JIT) compiler, and then the bytecode is compiled into native code at runtime. The most important tools are the Java compiler (javac.exe) and the Java interpreter (java.exe), which you use to run Java programs. The SDK also includes the base classes (called the Java platform), which provide you with the basic functionality and APIs you need to start writing applications.
Sun Microsystems has released an SDK for each of its five major releases of the Java platform. We recommended you get the latest SDK version (Java 1.4.2) for this article. The Java SDK is freely available. If you don't have it already, download it now (see Resources).
You can reference the Java 2 Standard Edition (J2SE) API documentation online (see Resources). It's a collection of HTML documents that you can navigate in a standard Web browser. The API documentation is an essential reference, and you'll probably use it frequently.
 |
Java SDK basics
- Write your Java source code in plain ASCII format with a text editor that supports long filenames.
- Save the file with a .java extension.
All Java keywords are lowercase. You can use any case for variable and method names, but you must use it consistently throughout a class.
- Compile your program from a command-line prompt, using the javac compiler that comes with the SDK. Always specify the .java extension.
- Run your program from a command-line prompt, using the Java interpreter that comes with the SDK. Do not specify the .class extension.
- Debug your code if necessary. Errors can occur when a Java program compiles or runs.
|
|
Installing the SDK
After you download the SDK, you need to install it on your machine. The installation is straightforward. If the setup gives you a choice between a typical or a custom install, choose the typical install. (You should choose the custom install only if you know exactly what you do and don't want to load on your machine.) The installation usually gives you the option of installing the source code for the standard Java platform classes. If you have sufficient disk space on your machine, we recommend that you take this option. These files will give you a chance to look at the implementation of the classes that make up the Java language and standard APIs. They're particularly well designed and implemented, and you can learn a lot from them.
After you install the SDK, you might need to configure it to work on your system. How you configure the SDK depends on your operating system and the SDK version you're using. The SDK includes complete installation and configuration instructions.
Your first Java program
Now you can write your first Java program -- the ubiquitous Hello World. Open your text editor and enter the source code exactly as you see it in Listing 1.
Listing 1. Hello World program
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
|
The Java language is case-sensitive, and language keywords such as class and public are always lowercase. You can use any case for your variable and method names, as long as you are consistent in the usage throughout a given class. When you're done typing, save the code in a file called HelloWorld.java. You have no choice when it comes to this filename. Java source files use a .java extension, and every Java source code file must have the same name as the class you define inside it. We can't state it enough: Case is significant, so file names like HELLOWORLD.JAVA and Helloworld.java will result in a compile error. You can save HelloWorld.java in any appropriate directory on your machine. You need to go to this directory to use the command-line tools, so make sure it's convenient.
Compiling the program
Now you're ready to compile the HelloWorld program. The Java language compiler that comes with the SDK is a command-line application called javac.exe. To compile a Java source code file, you simply pass the name of the .java file to the javac.exe program. To compile your HelloWorld program, open a command-line prompt and change your directory to the location where you saved the HelloWorld.java file. Then execute this command:
Like a Visual Basic compiler, the Java compiler can generate any number of errors. Naturally, you need to correct any errors before the Java compiler will compile the HelloWorld program successfully. A successful compile generates a class file called HelloWorld.class. This file represents the executable you run in the Java interpreter.
Running the program
The Java interpreter that comes with the SDK is a command-line application called java.exe. To run a Java bytecode executable, you simply pass the name of the Java program to the Java interpreter. Don't specify the .class extension when you use the Java interpreter. The interpreter expects only class files, so adding the .class extension will produce an error. To run your HelloWorld program, open a command-line prompt and change your directory to the location where you compiled the HelloWorld.java file. Your bytecode executable file, HelloWorld.class, should be there. Then execute this command:
The Java interpreter tries to execute the HelloWorld program's main() method. Java methods that list void as the return type are equivalent to a Sub in Visual Basic. Methods with other return types are equivalent to a Function in Visual Basic.
The Java interpreter might report a runtime error, which usually terminates program execution. As in Visual Basic, Java runtime errors are more difficult to debug than compile-time errors but occur less frequently. When runtime errors do occur, you can handle them gracefully because Java programs execute in a managed environment, which reduces the likelihood of "runaway code" bringing an entire machine to a screeching halt.
Java 101 from a Visual Basic perspective
Now that you have a basic idea of what Java code looks like and how to compile and run it on your test machine, you're ready to go deeper into the Java language's structure and syntax, including the Java programming environment and Java primitive data types. Because you're familiar with programming in Visual Basic, you'll learn by comparison. We'll discuss the Java platform's fundamental components in terms of their relation to or differences from similar components underlying the Visual Basic programming framework. If you're a C/C++ programmer, you can skip this section and take advantage of the tutorial "Java programming for C/C++ developers" instead (see Resources).
Visual Basic execution environment
Visual Basic is a high-level programming language; its purpose is to make it easier for human beings to develop computer programs. Computers can't understand high-level languages; they can only understand low-level machine languages -- a sequence of binary instructions that can execute directly on a computer's processor. For this reason, programs written in high-level languages must be translated into machine-language programs -- executables -- before they can be run on a computer. Different computers use different machine languages. An executable that runs on one machine won't run on another machine that uses a different machine language.
Two methods are available for translating a high-level programming language into machine-language executables: compilation and interpretation. Compilation involves translating an entire high-level program into a whole machine-language program, which can then be executed in its entirety. Interpretation involves translating a high-level program into machine instructions line-by-line; one line is translated and executed before the next line is reached. Compilation and interpretation are logically equivalent, but compiled programs tend to execute faster than interpreted programs. Visual Basic programs are compiled into machine-language executables by a program called a compiler.
The Java execution environment
Like Visual Basic programs, Java programs are compiled. Unlike Visual Basic programs, Java programs aren't compiled to a platform-specific machine language. Instead, they're compiled to a platform-independent language called bytecode. Bytecode is similar to machine language, but bytecode isn't designed to run on any real, physical computer. Instead, it's designed to be run by a program, called a Java virtual machine (JVM), which simulates a real machine.
Simply put, the JVM is an interpreter that translates Java bytecode into real machine-language instructions that execute on the underlying, physical machine. More specifically, the term Java virtual machine is used generically to refer to any program that executes Java class files. The Java interpreter program, java.exe, is a specific JVM implementation.
The Java platform uses the virtual-machine layer to ensure that programs written in the Java language are platform independent. Once a Java program is compiled to bytecode, it can run on any system that has a JVM. This includes UNIX, Linux, Windows, and many others. Programs in other languages must be recompiled for each platform on which they are to be executed, but a Java program needs to be compiled only once.
Data types
The Java language has two different types of data: the classes programmers define (or made available to them as part of the SDK or third-party class libraries), and "primitive" types (boolean, char, byte, short, int, long, float, and double) that the Java runtime understands directly. Most of the Java primitives have an equivalent in Visual Basic, and user-defined classes are like classes in Visual Basic, for the most part. Table 1 lists the primitive data types in the Java language and the equivalent type in Visual Basic.
Table 1. Java language primitive types and their Visual Basic equivalents
| Java primitive | Range | Visual Basic type | Notes | boolean | true, false | Boolean | The only valid values for booleans are true and false. | char | 0 - 65535 | String (of length 1) | The Java language uses Unicode to encode characters. | byte | 8-bit integer (signed) | Byte | | short | 16-bit integer (signed) | Integer | | int | 32-bit integer (signed) | Long | | long | 64-bit integer (signed) | N/A | | float | 32-bit floating point number | Single | | double | 64-bit floating point number | Double | | | N/A | | Variant | The Java language doesn't have a Variant type. | | N/A | | Date | The Java language doesn't have a primitive date type. You can use the Date class instead. | | N/A | | Currency | The Java language doesn't have a primitive currency type. You can use the BigDecimal class instead. |
Listing 2 shows some examples of declaring primitives in the two languages.
Listing 2. Declaring primitive types
Visual Basic Java
Option Explicit // Note that all Java
Dim LetterJ As String, I As Integer, x As Byte // variables must be declared
Dim Point1 As Single, Point2 As Double // before they can be used
Dim IsEmpty As Boolean char letterJ = 'j';
LetterJ = "j" int i = 0;
I = 0 byte x = 12
X = 12 boolean isEmpty = false;
IsEmpty = False float point1 = 1.1F;
Point1 = 1.1 double point2 = 0.0025;
Point2 = 0.0025
|
Operators
Operators in Visual Basic and the Java language share many similarities and have some key differences. The Java language uses the same set of operators that C and C++ use. Table 2 lists the most common operators used in both languages.
Table 2. Operators in Visual Basic and the Java language
| Java operators | Description | Usage | Visual basic operators | Notes | ++ | Increment | ++num num++ | N/A | This unary operator allows incrementing a non floating point number. | -- | Decrement | --num num-- | N/A | This unary operator allows decrementing a non floating point number. | * / | Multiplication Division | num * num
num / num | * / | |
/ | Integer division | num/num | \ | The Java language uses the same operator for integer division and arithmetic division. If the operands are integers, then integer division is performed. | % | Modular division | num % num | Mod | | +
- | Addition Subtraction | num + num num - num | + - | | + | String concatenation | str + str | & | | < <= | Less than Less than or equal | expr < expr expr <= expr | < <= | | > >= | Greater than Greater than or equal | expr > expr expr >= expr | > >= | | != | Inequality | expr != expr | <> | | == | Equality (primitives) | expr == expr | = | | == | Equality (objects) | obj == obj | Is | | ! | Logical negation | !boolean | Not | Visual Basic uses the same operator for bitwise negation on integers and logical negation on Boolean expressions. The Java language doesn't. | ~ | Bitwise negation | ~int | Not | | & | Bitwise AND Boolean AND | int & int
expr & expr | And | | | | Bitwise OR Boolean OR | int | int expr | expr | Or | | ^ | Bitwise XOR Boolean XOR | int ^ int expr ^ expr | Xor | | && | Conditional AND | if (expr &&expr)... | And | The Java language uses a different operator for a conditional AND and a logical Boolean AND. | || | Conditional OR | if (expr || expr) ... | Or | The Java language uses a different operator for a conditional OR and a logical Boolean OR. | | N/A | Pattern matching | str Like pattern | Like | You can use the Java String object's methods to duplicate this Visual Basic operator's functionality. |
Visual Basic functions and sub procedures vs. Java methods
Visual Basic lets you define functions and sub procedures. The main difference between them is that a sub procedure doesn't return a value, and a function does. In Java terminology, functions are called methods. The Java language has no equivalent to sub procedures in Visual Basic. However, in the Java language you can define a method that has no return value by using the keyword void, which is roughly equivalent to a sub procedure. You can declare Java methods only as members of a class; you can't define a method outside of a Java class. Listing 3 shows an example of one Java method that returns a value and another that doesn't.
Listing 3. Return types of Java methods
public class MyClass {
// This method doesn't return a value
public void myMethod1(String arg) {
...
}
// This method returns an integer
public int myMethod2(String arg) {
int i;
...
return i ;
}
|
Arrays
In the Java language, arrays are objects with attributes, the most important of which is the length attribute, which you can use to determine the array's size. Java arrays are always indexed from 0, and the array's declared size includes the 0th element. So a size of 100 means that the valid indexes are 0 through 99. Also, you bind the bracket characters ([ ]) you use to indicate arrays to the array type, not the array name. The Java language allows for array literals that let you initialize arrays to a predetermined set of values. Listing 4 shows some examples.
Listing 4. Arrays
Visual Basic Java
'An array with 100 integers // An array of 100 integers
Dim a(99) As Integer int[] a = new int[100];
'An array of Strings initialized // An array of Strings initialized
b = Array("Tom","Dick", "Harry") String[] b = {"Tom","Dick", "Harry"};
'Iterating through an array // Iterating through an array of length 100
' of length 100 int [] c = new int [100];
Dim c(99) As Integer for (int i = 0; i <.length; i++) {
For i=0 To UBound(c) c[i] = i;
c(i) = i }
Next
|
Strings
Visual Basic has a String data type to represent strings. You represent Java strings by objects of the String class. Both Visual Basic and Java string literals are represented as a sequence of characters enclosed within quotation marks. In the Java language, you have two ways to create a String object: You can use a string literal, or you can use a constructor. String objects are immutable, which means that once you give a String an initial value, you can't change it. In other words, if you want to change the value of a String reference, you need to assign the reference a new String object. Because Java strings are objects, you interact with them through the interface defined by the String class. (You'll learn more about constructors and interfaces in An introduction to object-oriented programming, later in this article.) The String class has a rich interface with quite a few useful methods.
Listing 5 demonstrates some of the most commonly used methods. Try compiling and running this example. Remember to name the source file StringTest.java, and don't forget that the filename's case is significant.
Listing 5. Strings in the Java language
/*
* The StringTest class simply demonstrates
* how Java Strings are created and how
* String methods can be used to create
* new String objects. Notice that when you
* call a String method like toUpperCase()
* the original String is not modified. To
* actually change the value of the original
* String, you have to assign the new
* String back to the original reference.
*/
public class StringTest {
public static void main(String[] args) {
String str1 = "Hi there";
String str2 = new String("Hi there");
// Display true if str1 and str2 have the value
System.out.println(str1.equals(str2));
// A new uppercase version of str1
System.out.println(str1.toUpperCase());
// A new lowercase version of str2
System.out.println(str1.toLowerCase());
System.out.println(str1.substring(1,4));
// A new version of str1 w/o any trailing whitespace chars
System.out.println(str1.trim());
// Display true if str1 start with "Hi"
System.out.println(str1.startsWith("Hi"));
// Display true if str1 ends with "there"
System.out.println(str1.endsWith("there"));
// Replace all i's with o's
System.out.println(str1.replace('i', 'o'));
}
}
|
The main() method
Java classes that are to be run from the command line as an application must define a main() method in order to run. In Java code, the main() method follows a strict naming convention. You declare main() methods this way:
public static void main(String[] args)
|
Note: You can reverse the public and static modifiers, and you can name the String array anything you like. However, that the above format is conventional. Not all classes need a main() method -- only those to be run from the command line. A typical Java application has one class with a main() method and several other supporting classes without a main() method.
Packages
Object-oriented languages like the Java language facilitate reuse of classes. Because most programmers use simple descriptive name for their classes (such as Invoice or User) the likelihood of a name collision is high when you reuse classes from various sources. The Java language addresses this issue by letting each class belong to a package. You can simultaneously use classes that have the same name but are in different packages. To associate a class with a package, you must make the package statement the first line of code in the class' source code. Here's an example:
package com.ibm.training;
|
By convention, you prefix package names by the reverse Internet domain name (for example, com.yourco.somepackage). To use a class that is in a different package, you have two choices. One option is to use the class's fully qualified name, including the package. Listing 6 shows an example.
Listing 6. Using fully qualified class names
public class PackDemo1 {
public static void main(String[] args) {
Java.util.Date today = new java.util.Date();
System.out.println("The date is " + today);
}
|
The alternative is to use an import statement in your source file for the class that's in another package. The fully qualified name isn't necessary anymore, as Listing 7 shows.
Listing 7. Using an import statement
import java.util.Date;
public class PackDemo1 {
public static void main(String[] args) {
Date today = new Date();
System.out.println("The date is " + today);
}
|
You can use a wildcard to import all the classes in a package. This is useful if you need to use several classes in the same package, as in Listing 8.
Listing 8. Using an import statement with wildcards
import java.util.*;
public class PackDemo1 {
public static void main(String[] args) {
Date now = new Date();
System.out.println("The date is " + today);
}
|
Packaging for reuse
In Visual Basic, you can write code and build it as a dynamic link library (DLL) that's represented on the file system as a file with a .dll extension. Other programs can refer to DLLs in order to use the code they contain. The Java language also allows you to package sets of classes, in file called a Java Archive (JAR), for reuse. You can combine sets of classes into a file with a .jar extension and reference the JAR file from other classes. Files with a .jar extension are standard zip files that can be manipulated with WinZip or other zip utilities. However, for convenience, the Java SDK contains a utility called jar.exe (on the Windows platform) that you can use to combine sets of classes into a single file with a .jar extension.
Before we walk you through an example of using the jar.exe utility, it's important that you understand the relationship between package names and the directory structure the Java platform uses to build classes and load them at runtime. Consider a class called Test whose source is in a file called Test.java. If you define Test.java to be part of the package com.mycompany.test, then the compiler creates a directory tree for the resulting .class module. The directory tree is based on the package name. In this case the tree is com\mycompany\test, with the periods in the package name converted to directory boundaries.
Now, open a command prompt and create a directory (for example, c:\javapack). Change to that directory (cd javapack). Use your favorite text editor to add the code in Listing 9 to a new file called Test.java.
Listing 9. An example of using packages
package com.mycompany.test;
public class Test
{
public static void main(String[] args) {
System.out.println("In test");
}
}
|
Now, compile Test.java with the following command. (The -d option should point to the directory that you created for this example):
java -d c:\javapack Test.java
|
You should now have a subdirectory called com in your c:\javapack directory. In fact, you can see that the fully qualified name of the comTest.class file that resulted from the compilation is Test.class. Note how the package name (com.mycompany.test) was translated to a corresponding directory structure (com\mycompany\test) rooted at the directory you specified with the -d option.
Now we'll show you how you can package Test.class for easy reuse by other classes. Run this command from the c:\javapack directory:
The command creates a file, called Test.jar, that contains all the classes under the com subdirectory.
Run this command to use the class in the Test.jar file:
java -classpath Test.jar com.mycompany.test.Test
|
Note that you have to use the fully qualified class name to run the command from the command line, and note the way you use the -classpath option to point to the Test.jar file. Alternatively, you could add the Test.jar file to the CLASSPATH environment variable, which is a semicolon-separated list of JAR files and directories the Java compiler and the JVM use to look for classes that need to be loaded.
Other differences
I've gone over the major syntactic differences between the Java language and Visual Basic. Some other differences are:
- Global variables: Unlike Visual Basic, the Java language offers no way to declare global variables (or methods).
GoTo: Although the Java language reserves goto as a keyword, it has no Goto statement like the one Visual Basic uses.
- Freely placed variables: You can declare Java variables anywhere, as they're needed. You don't need to group variables at the top of a block, as you must in Visual Basic.
- Inheritance: Visual Basic doesn't let you define classes that extend the capabilities of other classes. The Java language lets you define a class that inherits all but the private members of a class. These new classes can extend the behavior of the class they inherit from as well as override the behavior of the inherited members. (You'll learn more about inheritance in the next section.)
An introduction to object-oriented programming
The Java programming language is object-oriented. Visual Basic has many object features but isn't strictly an object-oriented language. In this section we'll show you how you build a class in Visual Basic and then the equivalent class in the Java language.
Working with classes
You can think of a class as a data type that you define. Variable instances of a class are called objects. Like other variables, objects have a type, a set of attributes, and a set of operations. An object's type is represented by the class from which the object was instantiated. The object's attributes represent its value or state. An object's operations are the set of possible functions you can invoke on the object in order to change its state.
Consider Visual Basic's Integer fundamental data type, which represents an integer. You use this type name to create variables that are instances of an integer. Every Integer variable has one attribute, which represents the integer number that the variable holds. Every Integer variable also has the same set of operations, which you can use to change the variable's state (or value). Some of the operations you can perform on an Integer variable include addition (+), subtraction (-), multiplication (*), division(\), and modulo (Mod).
Defining a Visual Basic class
Now, let's examine a situation in which you might want to develop your own type -- one that represents a complex object that the Visual Basic language doesn't support as a fundamental type. Suppose you're part of a team developing software for a financial institution, and your job is to develop code to represent a typical bank account. A bank has several accounts, but each account has the same basic set of attributes and operations. In particular, an account has a balance and an ID number. The Visual Basic code in Listing 10 defines an account class. It defines three operations: Deposit, Withdrawal, and InitAccount (used to initialize the balance and account number). Note how you keep track of the actual balance by using a private variable and that you define a property called Balance to let this class's user obtain the balance.
Listing 10. Defining a Visual Basic class
Private theBalance As Currency
Private theAccountNumber As Integer
Public Sub InitAccount (number As Integer, initBal As Currency)
theAccountNumber = number
theBalance = initBal
End Sub
Public Sub Deposit (amount As Currency)
theBalance = theBalance + amount
End Sub
Public Sub Withdrawal (amount As Currency)
theBalance = theBalance - amount
End Sub
Public Property Get Balance() As Currency
Balance = theBalance
End Property
Public Property Get AccountNumber() As Integer
AccountNumber = theAccountNumber
End Property
|
Defining a Java class
Listing 11 implements the Account class in the Java language.
Listing 11. The Java language Account class
public class Account {
private double balance;
private int number;
public Account(int number, double balance) {
number = argNumber;
balance = argBalance;
}
public void deposit(double amnt) {
balance += amnt;
}
public void withdrawal (double amnt) {
balance -= amnt;
}
public double getBalance() {
return balance;
}
public int getNumber() {
return number;
}
}
|
As you can see, defining a Java class is different from defining a Visual Basic class. The two languages' respective account classes reflect the major differences:
- You don't need a separate method to initialize an instance of
Account in your Java code. You use a constructor. As the name implies, you use it to construct instances of a class. A constructor must have the same name as the class for which it's defined, and it can take parameters. You can create as many constructors as you like for a class. If you don't provide a constructor, then a default constructor that takes no parameters is made available to you automatically. You could use the constructor in Listing 11 this way:
Account myAccount = new Account(12345, 0.00);
|
- Unlike Visual Basic, the Java language has no special provisions for properties. By convention, Java properties are private fields and you usually provide a set of methods called accessors for access to the fields that contain properties. The methods that return the properties' values are known as getters, those you use to set a property's value are called setters. Here's an example of a setter method:
public void setIntProperty(int argIntProperty) {intProperty = argIntProperty;} |
- The default access modifier for class members isn't
public as it is in Visual Basic (more on access modifiers later).
Benefits of objects
Three of the primary benefits of using classes and objects in an object-oriented language like the Java language are encapsulation, inheritance, and polymorphism:
- Encapsulation (or information hiding) refers to treating an object like a "black box"; that is, you can use an object without knowing (or caring) how it's implemented. Using objects through the interface defined by the methods (operators) defined in the class ensures you can change the class implementation without breaking any code that uses objects of that class.
- Polymorphism refers to the ability to associate different features to the same name, together with the ability to choose the right feature based on context. The most common example of polymorphism is method overloading, whereby you can define several methods that have the same name, as long as they take different parameters.
- Inheritance refers to reusing code by writing new classes that extend an existing class. For example, let's say you want to write a new class to represent a checking account. Because a checking account is a special kind of bank account, you can write the
CheckingAccount class so that it extends (or subclasses) the Account class. Then, the CheckingAccount class automatically gets the state and all the operators (functions) of the Account class. You only need to add the new state and operators specific to the CheckingAccount class. For example, you might add a cashCheck() function to perform the operation of cashing a check written for the checking account. If required, you could also change the subclass's inherited state or behavior. For example, a user might be allowed to overdraw on her checking account, so you would need to override the withdrawal function.
 |
Java classes in depth
Now that you understand the general role of classes and objects in an object-oriented programming framework, you're ready to delve more deeply into these specifics of the Java platform's class structure and implementation:
- Class members: A class member is always either a field or a method. A field represents data, and a method represents operations. Classes can define any number of members.
- Access modifiers: You declare class members with access modifiers, which specify the accessibility of the member outside of the class in which it is defined. For example, members that are declared private cannot be accessed at all, but public members are freely accessible.
- Objects: Classes are really just definitions. What you actually use in code are class instances called objects. You'll learn how to create objects from classes.
- Constructors: A constructor is a special operator you use to create objects. In general, a class isn't of much use if you can't create objects of that class. Constructors are important because they provide the ability to create new class instances.
- The
this keyword: Java objects implicitly reference themselves. It's important that you understand how to use the this keyword for this purpose.
Class members
A Java class is an independent module of code that defines attributes and operations in terms of members. Fields and methods are examples of members.
A field is a variable you declare inside of a class. Java fields come in two varieties: instance variables and class variables. Instance variables are associated with each instance of a class, and each instance has its own copies of instance variables. Class variables, which you declare using the static keyword, are associated with the class as a whole, and the class shares a single class variable with all class instances. For example, the balance field in a BankAccount is an instance field because each BankAccount instance has its own balance, which is independent of every other Account object's balance. On the other hand, you could declare an interest field as a class field because every BankAccount object uses the same interest rate.
A method is a function you declare inside of a class. Java methods come in two varieties: instance methods and class methods. Every class instance gets its own copy of instance methods, but there is only one copy of a class method, which all class instances share. You declare class methods using the static keyword. You use instance methods to operate on instance variables, and class methods to operate on class variables. For example, a deposit() method in the BankAccount class would be an instance method because each BankAccount has its own balance field, which the deposit() method would change. You could declare a setInterest() method as a class method because every BankAccount shares a single interest field, which the setInterest() method would change.
The program BankAccount in Listing 12 has five members. Two members are fields: balance, which is an instance field, and interest, which is a class field. Three members are methods: deposit() and withdraw() are instance methods, and setInterest() is a class method. Notice that you use an object name to access instance members, and the class name to access class members.
Listing 12. The BankAccount class
public class Account {
public class BankAccount {
private float balance; // an instance field
private static float interest; // a class, or static, field
// an instance method
public void deposit(float amount) {
balance += amount;
}
// an instance method
public void withdraw(float amount) {
balance -= amount;
}
// a class, or static, method
public static void setInterest(float interestRate) {
interest = interestRate;
}
public static void main(String[] args) {
// create a new account object
BankAccount account = new BankAccount();
// deposit $250.00 into the account
account.deposit(250.00F);
// set interest rate for all BankAccount objects
BankAccount.setInterest(5.0F);
}
}
|
Access modifiers
Like Visual Basic, the Java language allows you to set a class member's visibility. Java members use the public modifier to indicate that a member is freely accessible both inside the class and outside. Java members use the private modifier to indicate that the member is accessible only inside the class. Outside of the class, private members are inaccessible.
Consider the BankAccount class again. Assume you want other programmers using BankAccount objects to change the balance by using the deposit() and withdraw() methods. You'll declare these methods to be public, so they can be invoked in code outside of the BankAccount class. However, you don't want other programmers to change the balance field directly, so you make the balance field private.
You might be wondering what the default access level is -- that is, the access level for a class member you don't declare with either the public or private modifier? You might guess that the default access level is public, the default access level in Visual Basic. In fact, the default access level in the Java language is called package access, because only classes in the same package have access to these class members. If you want to declare a member with package access, don't use an access modifier keyword.
The Java language defines one further access level, called protected. You use the protected modifier when you want a member to be accessible in subclasses. We'll discuss protected class members a little later in the article.
Creating objects
If you look at the main() method of the BankAccount class in Listing 12, you can see that the code created a new BankAccount object, like this:
BankAccount account = new BankAccount();
|
First, you declare an object (that is, a variable) of type BankAccount. As you can probably guess, the new keyword sets aside enough memory to create a new object. The new object is actually created by this statement: BankAccount(). This statement looks like a method call. However, Listing 12 didn't declare a method with this name, so you might wonder what the statement is doing.
In fact, the statement is a constructor call. You can't create Java objects without a constructor, so if you write a class without a constructor, the compiler creates a default one. That's why you can call BankAccount(), even if you don't explicitly write a constructor in the BankAccount class.
 |
Java classes: A quick review
Class members: Java class members are fields and methods. Fields represent data, and methods represent operations. A class is a declaration of a class of objects, which are defined in terms of the class's members.
Access modifiers: You use access modifiers to limit the visibility of class members and constructors outside of the class in which they're defined. Most often, you'll encapsulate all the data in a class by declaring class fields private, and you'll define the interface of a class by writing public methods.
Constructors: You define constructors as a way to let other programmers create instances of your class. Generally, you'll define constructors that make it easy for another programmer to create an object that's initialized properly.
|
|
Visual Basic supports the concept of a constructor by letting you define a procedure called Class_Initialize for each class, but, unlike the Java language, it doesn't allow you to pass parameters to it.
Java constructors don't have return types; all constructors implicitly return a new object of the class in which it is defined. Every Java constructor must have the exact same name as the class in which it is declared. Otherwise, constructor declarations would be pretty much the same as method declarations. In particular, constructors can take parameters, just like Java methods.
Strictly speaking, a constructor isn't a kind of method, because methods are class members and constructors aren't. Class members, like fields and methods, are inherited in subclasses. Constructors are never inherited.
Explicit references
The Java language uses the this keyword to reference the current object. You can use the this keyword to refer explicitly to fields, methods, and constructors in the current class.
A common use for the this keyword is to resolve variable-scope issues. For example, the BankAccount class has a field called balance. Let's say you want to write a method called setBalance(float balance), which sets the object's balance field. The problem is that inside of the setBalance(float balance) field, when you refer to balance, you're actually referring to the balance parameter, not the balance field. You can refer to the field explicitly by using the this keyword, as in Listing 13.
Listing 13. The "this" keyword
public class Account {
public void setBalance(float balance) {
this.balance = balance;
}
|
Inheritance
Inheritance is one of the most important benefits of object-oriented programming. It's important that you understand inheritance correctly in order to use it to the greatest effect. Inheritance involves these key concepts:
- The
extends keyword: Inheritance is defined when a class is declared. You use the extends keyword to specify the superclass of the class you're writing.
- Constructors: Constructors aren't inherited in subclasses, but you often invoke the constructors of superclasses in your subclass's constructors.
- Overloading/overriding: Overloading refers to writing several methods with the same name but different parameters. Overriding refers to changing the implementation of a method that's inherited in a subclass.
- The
Object class: All Java objects ultimately inherit from the Object class, which defines the basic functionality that every Java object is guaranteed to have.
- Interfaces: An interface is a description of behavior that provides no implementation.
Extending classes
In Visual Basic, a class can't inherit from any other class, but the Java language allows single inheritance. Inheritance is a way to reuse code. When class A inherits from, or extends, class B, class A automatically inherits all of the public and protected members of class B. If class A is in the same package as class B, class A also inherits all of the members with default, or package, access. It's important to note, however, that subclasses never inherit the private members of the classes they extend.
Once you extend a class, you can add new fields and methods that define the attributes and operations that make your new class distinct from the superclass. Also, you can override the operations of the superclass that must behave differently in the subclass.
You can explicitly extend a class when you define it. To extend a class, you simply follow the name of the class with the extends keyword and the name of the class you want to extend. If you don't explicitly extend a class, the Java compiler automatically extends the class Object. In this way, all Java objects are ultimately subclasses of the Object class.
An extension example
Suppose you want to create a new CheckingAccount class. A CheckingAccount is a special kind of BankAccount. In other words, a CheckingAccount has the same attributes and operations as a BankAccount. However, a CheckingAccount also has an added operation -- cashing a check. So you can define your CheckingAccount class so that it extends BankAccount and add a cashCheck() method, as in Listing 14.
Listing 14. Extending classes
public class CheckingAccount extends BankAccount {
public void cashCheck(float amount) {
withdraw(amount);
}
}
|
Subclass constructors
Constructors aren't really members of a class, and constructors aren't inherited. A BankAccount constructor creates BankAccount objects, so you can't use it in the CheckingAccount class to create CheckingAccount objects. You can, however, use constructors from a superclass to initialize the parts of a subclass that are inherited. In other words, you often need to call superclass constructors in subclass constructors to partially initialize your subclass objects. You can do this by using the super keyword, followed by a parameterized list representing the arguments of the super class constructor you want to call. If you're using the super keyword in a constructor to call a superclass constructor, it must appear as the first statement in the constructor body.
For example, you need to write a CheckingAccount constructor to initialize CheckingAccount objects. You want to create CheckingAccount objects with an initial balance, so you pass in a dollar amount. This is just like a constructor in the BankAccount class, so you'll use that constructor to do all the work for you, as in Listing 15.
Listing 15. Subclass constructors
public class CheckingAccount extends BankAccount {
public CheckingAccount(float balance) {
super(balance);
}
public void cashCheck(float amount) {
withdraw(amount);
}
}
|
You can also use the super keyword to explicitly refer to superclass members from a subclass.
Overloading and overriding
The Java language allows you to define several methods with the same name, as long as they take different parameters. For example, Listing 16 defines a second cashCheck() method that takes the amount of the check to be cashed and a fee to be applied for the service. This is called method overloading.
Listing 16. Method overloading
public void cashCheck(float amount) {
withdraw(amount);
}
public void cashCheck(float amount, float fee) {
withdraw(amount+fee);
}
|
Often, when you create a subclass, you want to override the behavior of a method inherited from a superclass. For example, let's say that one difference between a CheckingAccount and a BankAccount is that a fee is applied when you withdraw money from a CheckingAccount. You need to override the withdraw() method in the CheckingAccount class so that a $0.25 fee is applied. You can define the CheckingAccount withdraw() method in terms of the BankingAccount withdraw() method, by using the super keyword, as shown in Listing 17.
Listing 17. Method overriding
public void withdraw(float amount) {
super.withdraw(amount+0.25F);
}
|
The Object class
The Object class is a special class in the Java class hierarchy. All Java classes are ultimately subclasses of class Object. In other words, the Java language supports a centrally rooted class hierarchy, and the Object class is the root class of that hierarchy. A similar concept exists in Visual Basic with Object variables that can be instantiated later to any type of class.
Because all Java objects inherit from the Object class, you can call the methods defined in Object for any Java object and expect similar behavior for each. For example, the Object class defines a toString() method, which returns a String object that represents the object. You can call the toString() method for any Java object and expect to get back a string representation of that object. Most class definitions override the toString() method so that it returns a specialized string representation for that particular class.
Another implication of having Object at the root of the Java class hierarchy is that all objects can be cast down to Object objects. In the Java language, you can define data structures that take objects of the Object class, and these data structures can hold any Java object.
Interfaces
I've already mentioned that a Java class allows only single inheritance, which means that a Java class can extend only one class. The designers of the Java language felt that multiple inheritance was too complicated, so instead they decided to support interfaces. An interface is like a class that can't be instantiated, with methods that are defined but not actually implemented.
You declare an interface just like a class, except you use the interface keyword instead of the class keyword. An interface can extend any number of superinterfaces. Methods inside an interface can't include an implementation. Interface methods are simply method definitions; they don't have bodies. This is the same concept that Visual Basic uses for interfaces; they consist of property and method declarations without code.
An Account interface
The code in Listing 18 shows how you might write a basic Account interface that defines a base set of functionality for bank accounts. Notice that there are no bodies for the methods you declare in an interface.
Listing 18. The Account interface
public interface Account {
public static final float INTEREST = 0.35F;
public void withdraw(float amount);
public void deposit(float amount);
}
|
Implementing interfaces
A Java class can extend only one class, but it can implement any number of interfaces. When a class implements an interface, it must implement every method defined in that interface.
Listing 19 defines a SavingsAccount class that implements the Account interface. Because the Account interface defines two methods -- withdraw(float amount) and deposit(float amount) -- the SavingsAccount class must provide an implementation for them. The SavingsAccount class can still extend another class, and it can implement any other interfaces, as long as they don't define the same members as the Account interface.
Listing 19. Implementing interfaces
public class SavingsAccount implements Account {
private float balance;
public SavingsAccount(float balance) {
this.balance = balance;
}
public void cashCheck(float amount, float fee) {
withdraw(amount+fee);
}
public void withdraw(float amount) {
balance += balance;
}
public void deposit(float amount) {
balance -= balance;
}
}
|
Taking stock
At this point, you should have a grasp of the Java language's essential components and comfortable writing simple Java programs. Specifically, you should be able to:
- Write a Java class with a
main() method, compile it, and run it.
- Write a Java interface and compile it.
- Write one or more constructors for your class.
- Write a class that extends another class and implements one or more interfaces.
- Create and use objects using the new keyword and constructor calls.
You should also be confident enough to begin examining and playing around with more advanced Java code. A good place to begin is with the Java platform classes themselves. The best way to gain experience using the language is to browse the API documentation (see Resources) and start writing programs that use these classes. Also, see the sidebar Honing your skills for some select resources to supplement this article.
ASP/COM and J2EE application models
Next we'll look at different application architectures that you can use to develop J2EE applications and relate them to corresponding ASP/COM application architecture.
Programming with Java Servlets: The basics
You write Java Servlets to allow programmatic control of a URL request from the browser. A typical servlet invocation goes something like this:
- The client makes a request to the Web server, naming a servlet as part of the URL -- for example
.
<FORM action="/myWebApp/LoginServlet" method="POST">.
- The Web server forwards the request to the application server, which locates an instance of the
servlet class.
- The Web container invokes the servlet. (A single instance of a servlet is loaded into memory, and each request invokes the single instance on a different thread.)
Note that the URL of the servlet in the HTML form includes the servlet's name and a prefix that's called the context root (in the above example, /myWebApp). The context root is roughly equivalent to an IIS virtual directory.
Figure 2 illustrates the steps.
Figure 2. Servlet invocation
Servlets extend the HttpServlet class. You can override the following methods of the HttpServlet class as needed:
init(): called when the application server loads a servlet
destroy(): called when the application server unloads a servlet
doGet(): called when a servlet is invoked via an HTTP GET
doPost(): called when a servlet is invoked via an HTTP POST
Writing a servlet involves writing the code to handle an HTTP request, processing any parameters, and returning HTML content directly or delegating another resource (like a JSP page) to handle the response. Returning HTML directly from a servlet isn't recommended, because managing HTML code inside a Java class is awkward.
When the application server calls the doGet() or doPost() methods, two objects are passed in as parameters:
HttpServletRequest allows access to any request parameters and other information from the HTTP request that caused the invocation of the servlet.
HttpServletResponse serves as the communication channel back to the client and allows the servlet to return content directly or return other HTTP return codes (errors, redirects, and so on).
Listings 20 and 21 show a couple of Hello World servlets. Listing 20 returns content directly, and Listing 21 uses a JSP page to return content.
Listing 20. HelloWorld servlet: Returning content directly
public class HelloWorldServlet extends HttpServlet {
/**
* Handles all HTTP POST requests
*
* @param request Object that encapsulates the request to the servlet
* @param response Object that encapsulates the response from the servlet
*/
public void doPost(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws ServletException, IOException {
try {
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("Hello World");
out.println("</html>");
} catch (Throwable t) {
...
}
}
}
|
Note that you get a PrintWriter from the response object and output the HTML to the client one line at a time.
Listing 21. HelloWorld servlet: Using a JSP page to return content
public class HelloWorldServlet extends HttpServlet {
/**
* Handles all HTTP POST requests
*
* @param request Object that encapsulates the request to the servlet
* @param response Object that encapsulates the response from the servlet
*/
public void doPost(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws ServletException, IOException {
try {
RequestDispatcher rd = getServletContext().getRequestDispatcher("helloworld.jsp");
rd.forward(request, response);
} catch (Throwable t) {
...
}
}
}
|
The RequestDispatcher is a wrapper around the resource that you want to forward the request to. Note that you include both the original request and response objects so that the target resource can have access to them. The ServletContext returned from getServletContext() allows the servlet to communicate with the underlying application server to get a RequestDispatcher. Note that all servlets have access to their ServletContext via the getServletContext() method.
Programming with JSP technology: The basics
JSP technology gives you the ability to do server-side scripting in the Java language. A JSP page is a composite page that consists of HTML and Java code that's processed by an application server before it's returned to the client. The application server processes the embedded code to produce static content prior to returning the page to the client. Like .aspx files, JSP page files typically look like HTML files with some additional tags and snippets of Java code.
The application server converts each JSP page into a special servlet when it's requested the first time. The servlet compiles and loads into memory. It services requests for the page for as long as the JSP source isn't modified. Upon source modification, the process starts over again and a new version of the servlet is produced.
You can use several special JSP tags in your JSP pages, and users can also define the behavior of tags they develop themselves. You can also add chunks of Java code to different parts of the page. The J2EE runtime environment makes a number of variables -- known as implicit variables -- available to you for these snippets of Java code. Examples of implicit variables are:
- request: the
HttpServletRequest associated with a particular invocation of the page
- response: the
HttpServletResponse associated with a particular invocation of the page
- out: a
PrintWriter associated with the HttpServletResponse
Listing 22 shows an example of a JSP page that contains a mix of HTML and Java code. The Java code portions between the <% and the %> tags are known as scriptlets.
Listing 22. HelloWorld servlet: Using a JSP page to return content
<html>
<title>JSP page example</title>
The date is:
<%
Date date = new Date();
out.println(date);
%>
Some more HTML
The value of <em>e</em> is:
<%
double e = Math.exp(1.0);
out.println(e);
%>
Yet even more HTML
The value of PI is:
<%
double pi = 22.0/7.0;
out.println(pi);
%>
</html>
|
Note the use of the JSP implicit variable out to write content back the client, and note the interleaving of HTML and Java code.
An example scenario
To illustrate the various architectural options we'll use a simple user-login scenario that consists of:
- A login page with an HTML form that collects a username and a password from the user
- Application logic that validates the user's credentials (possibly using a database) and redirects to the site's main page
This simple example lets us illustrate a variety of application architectures.
"Spaghetti" code
On the ASP side, a "spaghetti" code approach uses a single .asp file to contain both the application logic and the HTML form. (We don't recommend this approach for real-world examples, because all the presentation logic and application logic would be a single file, preventing you from reusing the code to validate the user's credentials.) The outline of the code looks something like the code in Listing 23. (We've left out any error-handling code to keep things simple.)
Listing 23. "Spaghetti" code in ASP
'--FUNCTION TO VALIDATE USER ENTERED LOGIN DATA FUNCTION ValidateUser(username,password)
'--Code to validate users credentials goes here
...
END FUNCTION %>
<html>
<head>
<title>Login example</title>
</head>
<body>
<% IF (REQUEST.SERVERVARIABLES("REQUEST_METHOD") = "GET") THEN %>
<form action="login.asp" method="post">
<!-- login form fields go here -->
</form>
<% ELSE userName = REQUEST.FORM("username") password = REQUEST.FORM("password")
IF (ValidateUser(userName, password)) THEN
RESPONSE.REDIRECT("mainpage.asp") ELSE ... END IF END IF %>
<font color="red"><b><%=LoginMessage%></b></font>
</body>
</html>
|
As Listing 24 shows, you can take the same approach in J2EE using a single JSP page to contain both the form and the application logic.
Listing 24. "Spaghetti" code in J2EE
<html>
<head>
<title>Login example</title>
<%!
private boolean validateUser(String userName, String password) {
...
}
%>
...
</head>
<body>
<%
if (request.getMethod().equals("GET") ) { %>
<form method="POST" target="login.jsp">
<!-- login form fields go here -->
</form> <% }
else {
String userName = request.getParameter("username");
String password = request.getParameter("password");
if (validateUser(userName, password)) {
response.sendRedirect("mainpage.jsp");
}
...
} %>
</body>
</html>
|
Like the ASP model, the JSP model isn't event-driven, so you need to check to see whether the form is being posted back by looking at the request and including the HTML for the form when it isn't a POST request. If it is a POST, you use a method declared in the JSP page to validate the login. Note the use of <%! to indicate that the code is a method. Scriptlets of code execute as they're encountered during JSP page processing, so the tags for scriptlets (<% %>) won't work for methods. Also note how you can easily include or exclude large chunks of HTML using an if/then/else programming construct. As in the ASP example, this isn't a recommended approach for J2EE development. The mixing of presentation code (HTML) and application logic allows very little reuse and makes the code difficult to maintain.
Improved "spaghetti" code
On the ASP side, a better approach builds on the previous example and uses an include ASP file containing only the validation logic in addition to the login.asp file. This lets you reuse the user-validation code in other ASP files.
A better approach on the J2EE side moves the application logic to a Java Servlet and limits the JSP page to the HTML components. This is an improvement because, as in the ASP improvement, the validation logic is now independent of the page displaying the form. Listing 25 shows how you can simplify the JSP page by putting the application logic in a servlet.
Listing 25. Improved "spaghetti" code in J2EE: JSP page
<html>
<head>
<title>Login example</title>
...
</head>
<body>
<form method="POST" target="LoginServlet">
<!-- login form fields go here -->
</form>
</body>
</html>
|
Listing 26 shows the validation code and navigation logic in a servlet.
Listing 26. Improved "spaghetti" code in J2EE: Java Servlet
public class LoginServlet extends HttpServlet {
/**
* Handles all HTTP POST requests
*
* @param request Object that encapsulates the request to the servlet
* @param response Object that encapsulates the response from the servlet
*/
public void doPost(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws ServletException, IOException {
try {
String userName = request.getParameter("username");
String password = request.getParameter("password");
if (validateUser(userName, password)) {
response.sendRedirect("mainpage.jsp");
}
...
} catch (Throwable t) {
...
}
}
private boolean validateUser(String userName, String password) {
...
}
}
|
The servlet in Listing 26 is the target of the form submission and acts like a controller -- handling user input and bringing up the appropriate page based on that input. Note that the HttpServlet parent class lets you handle both GET and POST requests by providing overridable methods (doGet() and doPost()).
The major drawback of this approach is that the credential-validation code (which would most likely access a database) is part of the servlet in the J2EE example. If different pages needed to use this logic, you'd have to duplicate it. Duplicate code is harder to maintain and more error prone because you must keep track of several copies.
Model-View-Controller approach
Now we'll show you a purer MVC approach to the example. On the ASP side, this entails moving the credential-validation logic to a separate COM component that you then access in ASP files. On the J2EE side, you move the credential-validation code to a separate class and access it from the servlet. The code snippets in Listing 27 show what the servlet would look like.
Listing 27. MVC in J2EE: Java Servlet
import com.ibm.businessobjects.UserValidation
public class LoginServlet extends HttpServlet {
/**
* Handles all HTTP POST requests
*
* @param request Object that encapsulates the request to the servlet
* @param response Object that encapsulates the response from the servlet
*/
public void doPost(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws ServletException, IOException {
try {
String userName = request.getParameter("username");
String password = request.getParameter("password");
UserValidation user = new UserValidation();
if (user.validate(userName, password)) {
response.sendRedirect("mainpage.jsp");
}
...
} catch (Throwable t) {
...
}
}
}
|
Listing 28 shows the credential-validation code in a separate class.
Listing 28. MVC in J2EE: User-validation class
package com.ibm.businessobjects;
public class UserValidation {
public boolean validate(String username, String password) {
...
}
}
|
The credential validation occurs inside a class that the servlet uses. Now the servlet doesn't need have to have any direct knowledge of how the validation is done. You're free to change the UserValidation class, and as long as you keep the same public interface, you don't need to change the servlet code. You could reuse this code in a non-Web application also.
Other J2EE components
The previous example showed how you separate out the business logic into a separate class or set of classes. This allows you to implement the business logic in a variety of ways without affecting the servlet and JSP page. Rather than use a business object that accesses a database (using JDBC), you can implement this logic in a variety of ways. For example:
- Use JCA to access legacy systems like mainframes: JCA is a J2EE API that allows a J2EE application to interact with a legacy system (such as CICS or IMS) using a standard interface.
- Use a Web services client API to access a Web service: This approach entails using Apache Axis or another Java Web services client API to access Web services.
- Use JMS to send the validation request as a message: This approach involves accessing JMS-compliant messaging middleware (like WebSphere MQ) using a standard API.
- Use EJB technology to implement the validation logic: This approach entails using EJB components -- instead of simple Java classes -- to implement the business logic. EJB components can use services in the application server to manage (database and other) transactions, manage method-level security, and provide clustering and caching. Delegating these sorts of tasks to the application server lets you focus on your business logic without having to worry about system-level issues.
 |
J2EE data access
Now we'll introduce you to the JDBC API, an industry-standard set of interfaces under the J2EE umbrella that lets Java programs access relational databases using a uniform set of API calls. JDBC lets you develop your applications once and then deploy them against any JDBC-compliant database -- DB2, Oracle, SQL Server, Sybase, mySQL, Informix, and others.
The JDBC API allows any Java component (including Java Servlets and JSP pages) to seamlessly access data from relational databases. JDBC consists of two parts: the API you use to access the database, and the pluggable JDBC drivers that database vendors provide for their respective databases. One of the most convenient features of the JDBC API is that you don't have to use any vendor-specific classes. You can specify the appropriate driver at runtime using a string constant and from that point on, you use the same programming interface regardless of which database you're using. This lets you switch out databases without having to change your code (as long as you provide the string constant that specifies the driver at runtime).
A JDBC example
A typical JDBC application must go through the following steps in order to |