Oracle® Database Java Developer's Guide 10g Release 1 (10.1) Part Number B12021-01 |
|
|
View PDF |
This book provides an overview on how to develop, load, and execute your Java applications in the Oracle Database.
This chapter contains the following information:
This chapter:
The following sections describe the additions to this release:
In this release, the system classes are upgraded from J2SE 1.3 to J2SE 1.4.1. J2SE 1.4.1 is compatible with J2SE 1.3. Sun Microsystems publishes the list of incompatibilities between J2SE 1.4.1 and previous versions at the following Web site:
http://java.sun.com/products/j2se/1.4.1/compatibility.html
As part of the upgrade of the system classes to J2SE 1.4.1, the OracleJVM supports Headless AWT. Headless AWT allows AWT computation, which does not rely on the native display and input devices of the platform to occur, but, instead, disallows attempts to access those native resources. Methods that attempt to display a graphical user interface or to read from keyboard or mouse input instead throw the new runtime exception java.awt.HeadlessException. Similarly, the OracleJVM disallows attempts to play or record sound using the server's native sound devices, but still allows applications to read, write and manipulate supported sound files. For more information, see "User Interfaces on the Server".
In Oracle Database, the OracleJVM has a new memory model for sessions that connect to the database through a dedicated server. Since a session using a dedicated server is guaranteed to use the same process for every database call, the Process Global Area is used for session specific memory and object allocations. This means that some of the objects and resources that used to be reclaimed at the end of each call can now live across calls. In particular, resources specific to a particular operating system, such as threads and open files, now are no longer cleaned up at the end of each database call.
For sessions that use shared servers, the restrictions across calls that applied in previous releases are still present. The reason is that a session that uses a shared server is not guaranteed to connect to the same process on a subsequent database call, and hence the session-specific memory and objects that need to live across calls are saved in the System Global Area. This means that process-specific resources, such as threads, open files and sockets must be cleaned up at the end of each call, and hence will not be available for the next call. For more details on OracleJVM behavior when using shared servers, see "Special Considerations for Shared Servers".
In Oracle Database, you can load a Web Services client stack into the OracleJVM to support callouts to external Web Services from Java as well as from PL/SQL. You can use the JPublisher tool to generate static Java client-proxies as well as PL/SQL call specifications on these proxies that are loaded into the OracleJVM to enable access to Web Services from Java, PL/SQL, and SQL code.
See Chapter 12, "Database Web Services" for more information and the Oracle Database JPublisher User's Guide for more information. For more details, see "Support for Calling Java Stored Procedures Directly".
In Oracle Database, you can now invoke public static methods of Java classes in the OracleJVM directly from Java clients without defining PL/SQL call specifications and calling these through JDBC. Instead, you can use the JPublisher utility to generate a client-proxy class with the same signature as the server-side Java class. Once you have instantiated a client-proxy instance with a JDBC connection, you can call the proxy methods directly.
Figure 1-1 demonstrates a client-side stub API for direct invocation of static server-side Java methods. JPublisher transparently takes care of stub generation.
Text description of the illustration nativeja.gif
For example, to call the following method in the server
public String oracle.sqlj.checker.JdbcVersion.to_string();
use
jpub -java=oracle.sqlj.checker.JdbcVersion
In certain enterprise applications it becomes essential to access Enterprise Java Beans (EJB) which are deployed on a remote server, from within the database. Oracle Database provides a means to access the remotely deployed EJBs over RMI.
Text description of the illustration ejbcallo.gif
Java has emerged as the object-oriented programming language of choice. It includes the following concepts:
The result is a language that is object-oriented and efficient for application-level programs.
This section covers some basic terminology of Java application development in the Oracle Database environment. The terms should be familiar to experienced Java programmers. A detailed discussion of object-oriented programming or of the Java language is beyond the scope of this book. Many texts, in addition to the complete language specification, are available at your bookstore and on the Internet. See "Suggested Reading" in the Preface for pointers to reference materials and for places to find Java-related information on the Internet.
All object-oriented programming languages support the concept of a class. As with a table definition, a class provides a template for objects that share common characteristics. Each class can contain the following:
When you create an object from a class, you are creating an instance of that class. The instance contains the fields of an object, which are known as its data, or state. Figure 1-3 shows an example of an Employee class defined with two attributes: last name (lastName) and employee identifier (ID).
Text description of the illustration classins.gif
When you create an instance, the attributes store individual and private information relevant only to the employee. That is, the information contained within an employee instance is known only for that single employee. The example in Figure 1-3 shows two instances of employee--Smith and Jones. Each instance contains information relevant to the individual employee.
Attributes within an instance are known as fields. Instance fields are analogous to the fields of a relational table row. The class defines the fields, as well as the type of each field. You can declare fields in Java to be static, public, private, protected, or default access.
The language specification defines the rules of visibility of data for all fields. Rules of visibility define under what circumstances you can access the data in these fields.
The class also defines the methods you can invoke on an instance of that class. Methods are written in Java and define the behavior of an object. This bundling of state and behavior is the essence of encapsulation, which is a feature of all object-oriented programming languages. If you define an Employee class, declaring that each employee's id is a private field, other objects can access that private field only if a method returns the field. In this example, an object could retrieve the employee's identifier by invoking the Employee.getId() method.
In addition, with encapsulation, you can declare that the Employee.getId() method is private, or you can decide not to write an Employee.getId() method. Encapsulation helps you write programs that are reusable and not misused. Encapsulation makes public only those features of an object that are declared public; all other fields and methods are private. Private fields and methods can be used for internal object processing.
Java defines classes within a large hierarchy of classes. At the top of the hierarchy is the Object class. All classes in Java inherit from the Object class at some level, as you walk up through the inheritance chain of superclasses. When we say Class B inherits from Class A, each instance of Class B contains all the fields defined in class B, as well as all the fields defined in Class A. For example, in Figure 1-4, the FullTimeEmployee class contains the id and lastName fields defined in the Employee class, because it inherits from the Employee class. In addition, the FullTimeEmployee class adds another field, bonus, which is contained only within FullTimeEmployee.
You can invoke any method on an instance of Class B that was defined in either Class A or B. In our employee example, the FullTimeEmployee instance can invoke methods defined only within its own class, or methods defined within the Employee class.
Text description of the illustration inherita.gif
Instances of Class B are substitutable for instances of Class A, which makes inheritance another powerful construct of object-oriented languages for improving code reuse. You can create new classes that define behavior and state where it makes sense in the hierarchy, yet make use of pre-existing functionality in class libraries.
Java supports only single inheritance; that is, each class has one and only one class from which it inherits. If you must inherit from more than one source, Java provides the equivalent of multiple inheritance, without the complications and confusion that usually accompany it, through interfaces. Interfaces are similar to classes; however, interfaces define method signatures, not implementations. The methods are implemented in classes declared to implement an interface. Multiple inheritance occurs when a single class simultaneously supports many interfaces.
Assume in our Employee example that the different types of employees must be able to respond with their compensation to date. Compensation is computed differently for different kinds of employees.
In traditional procedural languages, you would write a long switch statement, with the different possible cases defined.
switch: (employee.type) { case: Employee return employee.salaryToDate; case: FullTimeEmployee return employee.salaryToDate + employee.bonusToDate ...
If you add a new kind of Employee, you must update your switch statement. If you modify your data structure, you must modify all switch statements that use it. In an object-oriented language such as Java, you implement a method, compensationToDate(), for each subclass of Employee class that requires any special treatment beyond what is already defined in Employee class. For example, you could implement the compensationToDate() method of NonExemptEmployee, as follows:
private float compensationToDate() { return super.compensationToDate() + this.overtimeToDate(); }
You implement FullTimeEmployee's method, as follows:
private float compensationToDate() { return super.compensationToDate() + this.bonusToDate(); }
The common usage of the method name compensationToDate() allows you to invoke the identical method on different classes and receive different results, without knowing the type of employee you are using. You do not have to write a special method to handle FullTimeEmployees and PartTimeEmployees. This ability for the different objects to respond to the identical message in different ways is known as polymorphism.
In addition, you could create an entirely new class that does not inherit from Employee at all--Contractor--and implement a compensationToDate() method in it. A program that calculates total payroll to date would iterate over all people on payroll, regardless of whether they were full-time, part-time, or contractors, and add up the values returned from invoking the compensationToDate() method on each. You can safely make changes to the individual compensationToDate() methods with the knowledge that callers of the methods will work correctly. For example, you can safely add new fields to existing classes.
As with other high-level computer languages, your Java source compiles to low-level machine instructions. In Java, these instructions are known as bytecodes (because their size is uniformly one byte of storage). Most other languages--such as C--compile to machine-specific instructions--such as instructions specific to an Intel or HP processor. Your Java source compiles to a standard, platform-independent set of bytecodes, which interacts with a Java virtual machine (JVM). The JVM is a separate program that is optimized for the specific platform on which you execute your Java code. Figure 1-5 illustrates how Java can maintain platform independence. Your Java source is compiled into bytecodes, which are platform independent. Each platform has installed a JVM that is specific to its operating system. The Java bytecodes from your source get interpreted through the JVM into appropriate platform dependent actions.
Text description of the illustration jvmlayer.gif
When you develop a Java program, you use predefined core class libraries written in the Java language. The Java core class libraries are logically divided into packages that provide commonly-used functionality, such as basic language support (java.lang), input/output (java.io), and network access (java.net). Together, the JVM and core class libraries provide a platform on which Java programmers can develop with the confidence that any hardware and operating system that supports Java will execute their program. This concept is what drives the "write once, run anywhere" idea of Java.
Figure 1-6 illustrates how Oracle Java applications sit on top of the Java core class libraries, which in turn sit on top of the JVM. Because the Oracle Java support system is located within the database, the JVM interacts with the Oracle database libraries, instead of directly with the operating system.
Text description of the illustration vmlayerc.gif
Sun Microsystems furnishes publicly available specifications for both the Java language and the JVM. The Java Language Specification (JLS) defines things such as syntax and semantics; the JVM specification defines the necessary low-level behavior for the "machine" that executes the bytecodes. In addition, Sun Microsystems provides a compatibility test suite for JVM implementors to determine if they have complied with the specifications. This test suite is known as the Java Compatibility Kit (JCK). The OracleJVM implementation complies fully with JCK. Part of the overall Java strategy is that an openly specified standard, together with a simple way to verify compliance with that standard, allows vendors to offer uniform support for Java across all platforms.
The Java language has key features that make it ideal for developing server applications. These features include:
The only reason that you are allowed to write and load Java applications within the database is because it is a safe language. Java has been developed to prevent anyone from tampering with the operating system that the Java code resides in. Some languages, such as C, can introduce security problems within the database; Java, because of its design, is a safe language to allow within the database.
Although the Java language presents many advantages to developers, providing an implementation of a JVM that supports Java server applications in a scalable manner is a challenge. This section discusses some of these challenges.
The Oracle RDBMS provides Java applications with a dynamic data-processing engine that supports complex queries and different views of the same data. All client requests are assembled as data queries for immediate processing, and query results are generated on the fly.
Several features make Java ideal for server programming. Java lets you assemble applications using off-the-shelf software components (JavaBeans). Its type safety and automatic memory management allow for tight integration with the RDBMS. In addition, Java supports the transparent distribution of application components across a network.
Thus, Java and the RDBMS support the rapid assembly of component-based, network-centric applications that can evolve gracefully as business needs change. In addition, you can move applications and data stores off the desktop and onto intelligent networks and network-centric servers. More important, you can access those applications and data stores from any client device.
Figure 1-7 shows a traditional two-tier, client/server configuration in which clients call Java stored procedures the same way they call PL/SQL stored procedures. (PL/SQL is an advanced 4GL tightly integrated with Oracle Database.) The figure also shows how the Oracle Net Services Connection Manager can funnel many network connections into a single database connection. This enables the RDBMS to support a large number of concurrent users.
Text description of the illustration two_tier.gif
Multithreading support is often cited as one of the key scalability features of the Java language. Certainly, the Java language and class libraries make it simpler to write multithreaded applications in Java than many other languages, but it is still a daunting task in any language to write reliable, scalable multithreaded code.
As a database server, Oracle Database efficiently schedules work for thousands of users. The OracleJVM uses the facilities of the RDBMS server to concurrently schedule Java execution for thousands of users. Although Oracle Database supports Java language level threads required by the JLS and JCK, using threads within the scope of the database will not increase your scalability. Using the embedded scalability of the database eliminates the need for writing multithreaded Java servers. You should use the database's facilities for scheduling users by writing single-threaded Java applications. The database will take care of the scheduling between each application; thus, you achieve scalability without having to manage threads. You can still write multithreaded Java applications, but multiple Java threads will not increase your server's performance.
One difficulty multithreading imposes on Java is the interaction of threads and automated storage management, or garbage collection. The garbage collector executing in a generic JVM has no knowledge of which Java language threads are executing or how the underlying operating system schedules them.
Garbage collection is a major feature of Java's automated storage management, eliminating the need for Java developers to allocate and free memory explicitly. Consequently, this eliminates a large source of memory leaks that commonly plague C and C++ programs. There is a price for such a benefit: garbage collection contributes to the overhead of program execution speed and footprint. Although many papers have been written qualifying and quantifying the trade-off, the overall cost is reasonable, considering the alternatives.
Garbage collection imposes a challenge to the JVM developer seeking to supply a highly scalable and fast Java platform. The OracleJVM meets these challenges in the following ways:
The footprint of an executing Java program is affected by many factors:
From a scalability perspective, the key to supporting many concurrent clients is a minimum per-user session footprint. The OracleJVM keeps the per-user session footprint to a minimum by placing all read-only data for users, such as Java bytecodes, in shared memory. Appropriate garbage collection algorithms are applied against call and session memories to maintain a small footprint for the user's session. The OracleJVM uses three types of garbage collection algorithms to maintain the user's session memory:
OracleJVM performance is enhanced by implementing a native compiler.
Java executes platform-independent bytecodes on top of a JVM, which in turn interacts with the specific hardware platform. Any time you add levels within software, your performance is degraded. Because Java requires going through an intermediary to interpret platform-independent bytecodes, a degree of inefficiency exists for Java applications that does not exists within a platform-dependent language, such as C. To address this issue, several JVM suppliers create native compilers. Native compilers translate Java bytecodes into platform-dependent native code, which eliminates the interpreter step and improves performance.
The following describes two methods for native compilation:
Oracle Database uses Ahead-of-Time compilation to deliver its core Java class libraries: JDBC code in natively compiled form. It is applicable across all the platforms Oracle supports, whereas a JIT approach requires low-level, processor-dependent code to be written and maintained for each platform. You can use this native compilation technology with your own Java code.
As Figure 1-8 shows, natively compiled code executes up to ten times faster than interpreted code. So, the more native code your program uses, the faster it executes.
Text description of the illustration interpre.gif
Refer to "Natively Compiled Code" for more information.
Another strong feature of Java is dynamic class loading. The class loader loads classes from the disk (and places them in the JVM-specific memory structures necessary for interpretation) only as they are used during program execution. The class loader locates the classes in the CLASSPATH and loads them during program execution. This approach, which works well for applets, poses the following problems in a server environment:
Problem | Description | Solution |
---|---|---|
Predictability |
The class loading operation places a severe penalty on first-time execution. A simple program can cause the OracleJVM to load many core classes to support its needs. A programmer cannot easily predict or determine the number of classes loaded. |
The OracleJVM loads classes dynamically, just as with any other Java virtual machine. The same one-time class loading speed hit is encountered. However, because the classes are loaded into shared memory, no other users of those classes will cause the classes to load again--they will simply use the same pre-loaded classes. |
Reliability |
A benefit of dynamic class loading is that it supports program updating. For example, you would update classes on a server, and clients who download the program and load it dynamically see the update whenever they next use the program. Server programs tend to emphasize reliability. As a developer, you must know that every client executes a specific program configuration. You do not want clients to inadvertently load some classes that you did not intend them to load. |
Oracle Database separates the upload and resolve operation from the class loading operation at runtime. You upload Java code you developed to the server using the loadjava utility. Instead of using CLASSPATH, you specify a resolver at installation time. The resolver is analogous to CLASSPATH, but allows you to specify the schemas in which the classes reside. This separation of resolution from class loading means you always know what program users execute. Refer to Chapter 11, "Schema Object Tools" for details on loadjava and resolvers. |
This section discusses some important differences between the OracleJVM and typical client JVMs.
Client-based Java applications declare a single, top-level method (main()) that defines the profile of an application. As with applets, server-based applications have no such "inner loop." Instead, they are driven by logically independent clients.
Each client begins a session, calls its server-side logic modules through top-level entry points, and eventually ends the session. The server environment hides the managing of sessions, networks, and other shared resources from hosted Java programs.
A server cannot provide GUIs, but it can supply the logic that drives them. The OracleJVM supports only the headless mode of the Abstract Windowing Toolkit (AWT). All AWT Java classes are available within the server environment and your programs can use AWT functionality, as long as they do not attempt to materialize a GUI on the server. For more information, see "User Interfaces on the Server".
The OracleJVM is oriented to Java application deployment, not development. You can write and unit-test applications in your favorite IDE, such as Oracle JDeveloper, then deploy them for execution within the RDBMS.
Note: See "Development Tools" for more information. |
Java's binary compatibility enables you to work in any IDE, then upload Java class files to the server. You need not move your Java source files to the database. Instead, you can use powerful client-side IDEs to maintain Java applications that are deployed on the server.
This section briefly describes the main components of the OracleJVM and some of the facilities they provide.
The Oracle Database Java virtual machine (JVM) is a complete, Java 2-compliant Java execution environment. It runs in the same process space and address space as the RDBMS kernel, sharing its memory heaps and directly accessing its relational data. This design optimizes memory use and increases throughput.
The OracleJVM provides a run-time environment for Java objects. It fully supports Java data structures, method dispatch, exception handling, and language-level threads. It also supports all the core Java class libraries including java.lang, java.io, java.net, java.math, and java.util. Figure 1-9 shows its main components.
Text description of the illustration jvm_comp.gif
The OracleJVM embeds the standard Java namespace in RDBMS schemas. This feature lets Java programs access Java objects stored in Oracle databases and application servers across the enterprise.
In addition, the OracleJVM is tightly integrated with the scalable, shared memory architecture of the RDBMS. Java programs use call, session, and object lifetimes efficiently without your intervention. So, you can scale OracleJVMand middle-tier Java business objects, even when they have session-long state.
The garbage collector is described in "Automated Storage Management With Garbage Collection". The native compiler is discussed in "Performance". The rest of the components are described in the following sections:
In addition, the following sections give an overview of the JDBC driver:
To store Java classes in an Oracle database, you use the command-line utility loadjava, which employs SQL CREATE JAVA statements to do its work. When invoked by the CREATE JAVA {SOURCE | CLASS | RESOURCE} statement, the library manager loads Java source, class, or resource files into the database. You never access these Java schema objects directly; only the OracleJVM uses them.
The OracleJVM includes a standard Java 2 (also known as JDK 1.2) Java compiler. When invoked by the CREATE JAVA SOURCE statement, it translates Java source files into architecture-neutral, one-byte instructions known as bytecodes. Each bytecode consists of an opcode followed by its operands. The resulting Java class files, which conform fully to the Java standard, are submitted to the interpreter at run time.
To execute Java programs, the OracleJVM includes a standard Java 2 bytecode interpreter. The interpreter and associated Java run-time system execute standard Java class files. The run-time system supports native methods and call-in/call-out from the host environment.
Note: You can also compile your code for faster execution. The OracleJVM uses natively compiled versions of the core Java class libraries and JDBC drivers. For more information, see "Natively Compiled Code". |
In response to requests from the run-time system, the Java class loader locates, loads, and initializes Java classes stored in the database. The class loader reads the class, then generates the data structures needed to execute it. Immutable data and metadata are loaded into initialize-once shared memory. As a result, less memory is required for each session. The class loader attempts to resolve external references when necessary. Also, it invokes the Java compiler automatically when Java class files must be recompiled (and the source files are available).
Java class files are fully portable and conform to a well-defined format. The verifier prevents the inadvertent use of "spoofed" Java class files, which might alter program flow or violate access restrictions. Oracle security and Java security work with the verifier to protect your applications and data.
JDBC is a standard set of Java classes providing vendor-independent access to relational data. Specified by Sun Microsystems and modeled after ODBC (Open Database Connectivity) and the X/Open SQL CLI (Call Level Interface), the JDBC classes supply standard features such as simultaneous connections to several databases, transaction management, simple queries, calls to stored procedures, and streaming access to LONG column data.
Using low-level entry points, a specially tuned JDBC driver runs directly inside the RDBMS, thereby providing the fastest access to Oracle data from Java stored procedures. The server-side internal JDBC driver complies fully with the Sun Microsystems JDBC specification. Tightly integrated with the RDBMS, it supports Oracle-specific data types, globalization character sets, and stored procedures. Additionally, the client-side and server-side JDBC APIs are the same, which makes it easy to partition applications.
One appeal of Java is its ubiquity and the growing number of programmers capable of developing applications using it. Oracle furnishes enterprise application developers with an end-to-end Java solution for creating, deploying, and managing Java applications. The total solution consists of client-side and server-side programmatic interfaces, tools to support Java development, and a Java virtual machine integrated with the Oracle Database server. All these products are 100 percent compatible with Java standards.
In addition to the OracleJVM, the Java programming environment consists of:
To help you decide which Java APIs to use, examine the following table:
Type of functionality you need | Java API to use |
---|---|
To have a Java procedure invoked from SQL, such as a trigger. |
Java Stored Procedures |
To invoke dynamic, complex SQL statements from a Java object. |
JDBC |
If you are a PL/SQL programmer exploring Java, you will be interested in Java stored procedures. A Java stored procedure is a program you write in Java to execute in the server, exactly as a PL/SQL stored procedure. You invoke it directly with products like SQL*Plus, or indirectly with a trigger. You can access it from any Oracle Net client--OCI, PRO* or JDBC. Chapter 5, "Developing Java Stored Procedures" explains how to write stored procedures in Java, how to access them from PL/SQL, and how to access PL/SQL functionality from Java.
In addition, you can use Java to develop powerful programs independently of PL/SQL. Oracle Database provides a fully-compliant implementation of the Java programming language and JVM.
You can invoke existing PL/SQL programs from Java and invoke Java programs from PL/SQL. This solution protects and leverages your existing investment while opening up the advantages and opportunities of Java-based Internet computing.
Oracle offers two different application programming interfaces (APIs) for Java developers to access SQL data--JDBC. Both APIs are available on client and server, so you can deploy the same code in either place.
JDBC is a database access protocol that enables you to connect to a database and then prepare and execute SQL statements against the database. Core Java class libraries provide only one JDBC API. JDBC is designed, however, to allow vendors to supply drivers that offer the necessary specialization for a particular database. Oracle delivers the following three distinct JDBC drivers.
For more information on JDBC, see "Utilizing JDBC for Querying the Database".
JPublisher provides a simple and convenient tool to create Java programs that access existing Oracle relational database tables. See the Oracle Database JPublisher User's Guide for more information.
The introduction of Java to the Oracle Database server allows you to use several Java Integrated Development Environments. The adherence of Oracle Database to Java compatibility and open Internet standards and protocols ensures that your 100% pure Java programs work when you deploy them on Oracle Database. Oracle delivers many tools or utilities, all written in Java, that make development and deployment of Java server applications easier. Oracle's JDeveloper has many features designed specifically to make deployment of Java stored procedures and Enterprise JavaBeans easier. You can download JDeveloper at the following site: http://otn.oracle.com/software/products/jdev/content.html.
With the introduction of Oracle Application Server Containers for J2EE (OC4J)--a new, lighter-weight, easier-to-use, faster, and certified J2EE container--Oracle began desupport of the Java 2 Enterprise Edition (J2EE) and CORBA stacks from the database, starting with Oracle9i database release 2. However, the database-embedded Java VM (OracleJVM) is still present and will continue to be enhanced to offer Java 2 Standard Edition (J2SE) features, Java stored procedures and JDBC in the database.
As of Oracle9i database release 2 (9.2.0), Oracle no longer supports the following technologies in the database:
Customers will no longer be able to deploy servlets, JSP pages, EJBs, and CORBA objects in Oracle databases. Oracle9i database release 1 (9.0.1) will be the last database release to support the J2EE and CORBA stack. Oracle is encouraging customers to migrate existing J2EE applications running in the database to OC4J now.