Oracle8i Java Developer's Guide
Release 8.1.5

A64682-01

Library

Product

Contents

Index

Prev Next

3
Writing Java Applications on Oracle8i

Oracle8i's JServer has been designed from the ground up as the world's first true enterprise-scale Java programming platform. The JServer runs standard Java applications and, therefore, supports powerful and familiar functionality and development techniques. However, the server emphasis makes Oracle8i somewhat different from typical client development environments. This chapter describes the basic steps in writing, installing, and deploying JServer applications.

You should refer to the detailed documentation for the different JServer APIs to fully explore their usage. The intent of this chapter is to place the APIs in an overall context, with enough detail for you to see how they fit together and how you use them in the JServer environment. The next chapter examines some details of the JServer environment, drawing contrasts to both Java client-side and PL/SQL development with which you may be familiar.

JServer Basics

As discussed in Chapter 1, the Oracle8i JServer platform is a standard compatible Java environment. It will execute any 100% pure Java application. It has been implemented by Oracle to be compatible with the Java Language Specification and the Java Virtual Machine Specification. It supports the standard Java binary format and the standard Java APIs. However, unlike other Java environments, the JServer is embedded within the world's most popular RDBMS and, therefore, introduces a number of new concepts.

This section introduces the basics necessary to build and deploy Java applications on Oracle8i. It then takes you on a quick tour of some simple Java applications and concludes with some finer details that are not necessary to get started but that you may need to know as you really start to exploit the full potential of this enterprise Java platform.

Call and Session

Chapter 1 introduced the concepts of call and session. This section elaborates on how call and session relate to the programs you write, the memory they use, and object lifetime.

JServer sessions are entirely analogous to traditional Oracle sessions, except that they scope Java's object memory. A session encompasses the lifetime of all the objects referenced by Java static variables, all the objects referred to by these objects, and so on (their transitive closure). From the point of view of a client/server interaction, each JServer session has its own private object memory. From your perspective, it appears as though a separate, individual Java virtual machine was invoked for each session, although the implementation is vastly more efficient than this seems to imply.

The creation and lifetime of a session is dependent upon the specific API you use to write an application. For the initial Oracle8i release, we provide three APIs--Java Stored Procedures, CORBA distributed objects, and Enterprise JavaBeans (EJBs). In addition, we demonstrate a simple HTTP server, illustrating how Oracle8i opens the database to multiple protocols beyond the familiar Oracle SQL*Net. In subsequent releases of Oracle8i, we will be exposing and documenting the Java-based framework for writing scalable servers in Java-- the same framework we use for the Aurora/ORB.

From a Java development perspective, the lifetime of a stored procedure session is identical to the SQL session in which it is embedded. This concept is familiar to PL/SQL users. Any state represented in Java transparently persists for the lifetime of the RDBMS session, simplifying the process of writing stored procedures, triggers, and methods for Oracle Abstract Data Types. Individual invocations of Java code within a session are known as calls. A call may be initiated by a SQL call of some kind, for example.

CORBA and EJB provide a more object-oriented style of message sending between clients and servers, although the distinction between client and server may be blurred in a general N-tier application. Even in the CORBA and EJB case, clients must implicitly or explicitly establish a session in the server. Every message you send on the client to a server-resident object initiates a call. Refer to the Oracle8i Enterprise JavaBeans and CORBA Developer's Guide and Reference for specifics, but note that the concepts of call and session apply across all uses of Oracle8i.

Schema and Rights

The classes that define a Java application are stored within the Oracle8i RDBMS under the SQL schema of their owner. Once installed, no access to the file system is necessary because these classes and other resources are dynamically loaded from the RDBMS as required to support a running application. By default, classes that reside in one user's schema are not executable by other users because of security concerns. The appropriate conditions can, of course, be granted to allow other users to access locally defined classes.

The JServer also provides two rights models under which classes run. By default, classes run under the effective identity and rights of the session in which they are called--invoker's rights. It is also possible to specify that classes can execute only under the effective identity and rights of their owner--definer's rights. You can use definer's rights, for example, to restrict access to private RDBMS tables or other server resources private to the definer of the code or, perhaps, to expand such access to a broader group of users explicitly granted access. Refer to the Oracle8i Java Stored Procedures Developer's Guide for more information.

Exposing Java Methods With Call Specifications

If you write a Java stored procedure that you intend to invoke with a trigger, directly or indirectly in SQL DML or in PL/SQL, then you must specify what method is accessible and how to access it, through a call specification. By their nature, Java programs consist of many methods in many classes, and only a few static methods will typically be exposed with call specifications (only a few "gateway" calls must be exposed to SQL users). On the other hand, if you are performing CORBA and EJB development, then you may never use call specifications directly. Oracle8i's CORBA and EJB implementations support standard CORBA and Java styles of exposing objects by name, with accompanying CORBA and Java-style specifications of the interfaces to those objects. For details on call specifications and their use, refer to the Oracle8i Java Stored Procedures Developer's Guide. Refer to the Oracle 8i Enterprise JavaBeans and CORBA Developer's Guide for details on the related concepts of EJB deployment descriptor and CORBA Interface Definition Language (IDL).

Where's main()?

Client-based Java applications furnish a single, top-level static method main(), which defines the overall profile of an application. As with today's applets, server-based applications do not own such an "inner loop" but are, instead, driven by logically separated clients. In essence, these clients own main(), either explicitly in client Java code or implicitly by the nature of the client-side program that interacts with the server. Clients initiate interactions (sessions), call their server-side logic only through top-level entry points, and ultimately terminate their sessions. These entry points are exposed through call specifications or standard CORBA and EJB mechanisms, as the preceding section discusses. The underlying server environment hides the details associated with session, network, state, and other shared resource management issues from hosted Java server code. This contract is significantly more complex than that of a traditional client virtual machine-- but then, this means that you need only write Java server logic, not the server itself! If appropriate, you can name your top-level entry point main() and write a call-spec for it, but you do not have to. In fact, you will probably find it makes more sense to expose more appropriately named methods for stored procedures, as in PL/SQL.

Security Basics

Oracle8i introduces some new ROLES in support of Java security. You should not run into any barriers in this area as you start out, but you should be aware of them. For example, JAVASYSPRIV is an ordinary database ROLE created when you install Oracle8i with Java. You can grant and revoke JAVASYSPRIV just as you would for any other ROLE. Some operations require only JAVAUSERPRIV to have been granted. For example creating a file requires JAVASYSPRIV, but reading and writing requires only JAVAUSERPRIV. In most cases, if the user executing the code in question has not been granted JAVASYSPRIV and that user attempts to perform a disallowed operation, then a java.lang.SecurityException will be thrown. We will discuss Java security in Oracle8i and these ROLES in detail later. They are crucial to understanding server-based Java.

User Interfaces on the Server

Oracle8i furnishes all core Java class libraries on the server, including those associated with presentation of user interfaces (for example, java.awt and java.applet). It is, however, inappropriate for code executing in the server to attempt to "bring up" or materialize a user interface in the server. To understand why this is inappropriate, imagine thousands of users worldwide exercising an Internet application that somehow enters into code that requires someone to click on a dialog presented on the server hardware.

You can write Java programs that reference and use java.awt classes, as long as you do not attempt to materialize a user interface. In fact, this is exactly how you would construct Java applet code. You build applets and test them using the java.awt and platform-specific Peer implementation, which is a platform-specific set of classes for support of a specific windowing system. When the user downloads an applet, it dynamically loads the proper client Peer libraries, and the user sees a display that is proper for the operating system or windowing system in use on the client side. Oracle8i takes the same approach. We provide an Oracle-specific Peer implementation that throws an exception, oracle.aurora.awt.UnsupportedOperation, if you execute Java code on the Oracle8i server that attempts to materialize a user interface.

Note that Oracle8i's lack of support for materializing user interfaces in the server means that we do not pass the Java Compatibility Kit (Version 1.1.6) tests for java.awt, java.awt.manual and java.applet. In the Oracle RDBMS, all user interfaces are supported only on client applications, although they may be displayed on the same physical hardware that supports the server--for example, in the case of Windows NT. Because it does not make sense for the server to support user interfaces, we exclude these tests from our complete Java Compatibility Kit testing. A similar issue exists for vendors of Java-powered embedded devices and in handheld devices (known as Personal Java). Future releases of Java and the Java Compatibility Kit will provide improved factorization of user interface support so that vendors of Java server platforms can better address this issue.

Java Source, Binaries, and Dependencies

All Java applications begin at some point with Java source code (typically .java files). However, Java's binary standard allows you to use third-party class libraries when building your applications. This means you may develop an application that makes use of Java code to which you have no source-level access.

As discussed in Chapter 2, the actual code that executes in Java applications is determined dynamically at runtime. In Sun's JDK, for example, the Java class loader loads the binaries (.class files) that it finds in the underlying file system, as specified in the CLASSPATH. Oracle8i enforces a separate resolution step where inter-class references are resolved based on the resolver you specify. The resolver is directly analogous to CLASSPATH, but it allows you to specify schemas within which classes are found. This separation of the resolution step from the class loader in Oracle8i enables you to ensure that the code loaded dynamically at runtime is predetermined and reproducible.

Once you have Java source code or binaries, you must load them on the server. Oracle supplies a utility, loadjava, for this task. Refer to the Oracle8i Java Stored Procedures Developer's Guide for detailed information on loadjava. The loadjava utility also enables you to load groups of source and/or binary files on the server, either individually by name or as held in a Java archive (.jar) file. In the latter case, loadjava operates behind the scenes to load the individual components within the .jar file on the server. Server-resident code has no knowledge of the .jar file it might have originally been loaded from.

Note, Oracle's JServer does not require access to the Java source that makes up an application, although you can load Java source on the server and use JServer's Java source compiler to compile it. Thus, it is possible to deliver and install applications entirely in standard Java binary form, entirely in source form, or in any combination of the two.

Dependency Management

JServer provides a sophisticated dependency management and automatic build facility that will transparently recompile source programs when you make changes to the source or binary programs upon which they depend. Consider the following cases:


   public class A
   {
      B b;
      public void assignB () {b = new B()}
   }
   public class B
   {
      C c;
      public void assignC () {c = new C()}
   }
   public class C
   {
      A a;
      public void assignA () {a = new A()}
   }

The system tracks dependencies at a class-level of granularity. In the preceding example, you can see that classes A, B, and C depend on one another because A holds an instance of B, B holds and instance of C, and C holds an instance of A. If you change the definition of class A by adding a new field to it, for example, then the dependency mechanism in Oracle8i will flag classes B and C as invalid. Before you use any of these classes again, Oracle8i will attempt to resolve them again and recompile, if necessary. Note that classes can be recompiled only if source is present on the server.

The dependency system enables you to rely on Oracle8i to manage dependencies between classes and recompile and resolve automatically. You must only force compilation and resolution yourself if you are doing development and you want to find out earlier if there are any problems. The loadjava utility also provides the facilities for forcing compilation and resolution if you do not want to allow the dependency management facilities to take care of this for you.

Invoking Your Java Program

We have reviewed the basics of writing and deploying Java applications on Oracle8i, but how do you actually invoke your Java program? Usually, Java developers perform this with:


java myprogram

where myprogram is the name of a class that contains a main() method. In the case of a Java stored procedure, your code will generally be executed as a by-product of some database manipulation in the case of a trigger or by a client program that performs a DML call of some type. In both of these cases, you must publish the Java methods that can be invoked, using a call specification. One simple way of invoking a method that has been published in this manner is to:


call myprogram.staticMethodName();

in SQL*Plus. Another way to invoke your Java program is through EJB or CORBA, allowing you to easily distribute program logic between clients and servers.

We will discuss both of these techniques in the examples that follow.

Some Simple Examples

We recommend that you approach Java development in Oracle8i incrementally, building on what you learn at each step. Your specific requirements may not currently include using CORBA and EJB, but having a basic understanding of how they work will prepare you to use these powerful tools when the time is right.

First, you should master the process of writing simple Java programs and deploying them in the server. You should understand the use of call specifications as a means to expose the top-level entry points for your Java program. We will concentrate on these areas by looking at a simple Java stored procedure that echoes "Hello world".

Second, you should understand how you access and manipulate SQL data from Java. Most Java server programs, and certainly Java programs executing on Oracle8i, will interact with database-resident data. The two standard APIs for accomplishing this are JDBC and SQLJ. Because JDBC forms the foundation for SQLJ, you should understand how the two work together, even though you may be using only SQLJ in your code. We will focus on JDBC and SQLJ in our second example. Every Java developer must review and understand this area.

Finally, if you intend to distribute Java logic between client and server or in an N-tier architecture, then you should understand how CORBA and EJB work in Oracle8i. CORBA and EJB provide the simplest solution to this difficult problem, in an Internet-standard manner, enabling you to leverage component-based development for transactional applications. Furthermore, EJB and CORBA build upon Oracle8i's facilities for Java stored procedures and JDBC to simplify your job.

At the end of our discussion of EJB and CORBA, we will take a quick look at a tool known as the session shell, an example of a tool written completely in Java using Java stored procedures and CORBA. It allows you to interact with server-resident objects visible through CORBA within your session, using Unix shell commands. The session shell is a useful tool that the Oracle8i Enterprise JavaBeans and CORBA Developer's Guide documents. However, it is also an important example of how the ability to execute Java on the Oracle8i server opens up a whole new world for Oracle developers. It demonstrates that you can use CORBA, which is fairly complex, to build tools that make life simpler for developers and your end users.

Hello World - An Introduction to Java Stored Procedures

Here is an example of writing "Hello world". Define a class, Hello, with one method, Hello.world(), that returns the string "Hello world":


public class Hello
{
   public static String world ()
   {
      return "Hello world";
   }
}

Compile the class on your client workstation. Using Sun's JDK, for example, you invoke the Java compiler, javac, as follows:


javac Hello.java

Normally, it is a good idea to specify your CLASSPATH on the javac command line, especially when writing shell scripts or make files. The Java compiler produces a java binary file--in this case, Hello.class. Now you must load the class on the Oracle8i server using loadjava.


loadjava -user scott/tiger Hello.class

This simplest of examples uses the default oci8 connection to the database. You must specify the username and password. By default, the logon schema (in this case, "scott") will contain the Hello class.

Keep in mind where this Java code will execute. If you execute Hello.class on your client workstation, then it will search the CLASSPATH you provide for all supporting core classes it must execute. One obvious class needed is String, which is part of the java.lang package. String must be found in the CLASSPATH (or, more typically, in a .jar or .zip file specified as part of the CLASSPATH).

In this case, you have loaded Hello.class to the server, where it is stored in the database as a Java schema object. When you execute the world() method of the Hello.class on the server, it will find the necessary supporting classes, such as String, using a resolver--in this case, the default resolver. The default resolver looks for classes in the current schema first and then in SYS schema. All core class libraries, including the java.lang package, are found in SYS schema. You may need to specify different resolvers, and you can force resolution to occur when you use loadjava, to determine if there are any problems earlier, rather than at runtime. Refer to Appendix A, "Tools", for more details on loadjava if you want to learn more than the basics necessary for this example.

To invoke a Java static method with a SQL CALL, you must publish it with a call specification. A call specification defines for SQL which arguments the method takes and which SQL types it returns. It would be simple to generate call specifications, but there is no need to have each Java method specified for interaction with SQL in this manner. It makes this simple example appear more complex, but in practice, few methods are exposed in this manner. Oracle's JDeveloper product, for example, automates call-specification creation.

In SQL*Plus, connect to the database and define a top-level call specification for Hello.world():


connect scott/tiger
create or replace function HELLOWORLD return VARCHAR2 as
   language java name 'Hello.world () return java.lang.String';
myString varchar2;
call HELLOWORLD() into :myString;
print myString;

The call HELLOWORLD() into :myString statement is a new way to make a top-level call in Oracle8i. The Oracle-specific select HELLOWORLD from DUAL also works. Note that SQL and PL/SQL see no difference between a stored procedure written in Java, PL/SQL, or any other language. The call specification provides a means to tie inter-language calls together in a consistent manner. Again, do not be intimidated or feel burdened by call specifications because they are necessary only for entry points invoked with triggers or SQL and PL/SQL calls. Furthermore, JDeveloper can automate the task of writing call specifications if you want. Finally, if you are developing server Java code using CORBA and EJB, you do not use call-specifications.

For more information on Java stored procedures, using Java in triggers, call specifications, rights models, and inter-language calls, refer to the Oracle8i Java Stored Procedures Developer's Guide. Keep reading here for an overview of SQLJ and JDBC for accessing SQL data and for a tour of CORBA and EJB.

Accessing and Manipulating SQL Data Using JDBC and SQLJ

JDBC is an industry-standard API developed by Sun Microsystems that allows you to embed SQL statements as Java method arguments. JDBC is based on the X/Open SQL Call Level Interface and complies with the SQL92 Entry Level standard. Each vendor, such as Oracle, creates its JDBC implementation by implementing the interfaces of the Sun Microsystems java.sql package. As discussed in Chapter 1, Oracle offers three JDBC drivers that implement these standard interfaces: 1) the JDBC Thin driver, a 100% pure Java solution you can use for either client-side applications or applets and requires no Oracle client installation; 2) the JDBC OCI drivers (OCI 8 or OCI 7), which you use for client-side applications and requires an Oracle client installation; and 3) the server-side JDBC driver embedded in the Oracle8i server.

For the developer, using JDBC is a step-by-step process of creating a statement object of some type for your desired SQL operation, assigning any local variables that you want to bind to the SQL operation, and then executing the operation. This process is sufficient for many applications but becomes cumbersome for any complicated statements.

SQLJ offers an industry-standard way to embed any static SQL operation directly into Java source code in one simple step, without requiring the individual steps of JDBC. (Dynamic SQL operations, where the operations are not known until runtime, require JDBC. In typical applications, however, this represents a minority of the SQL operations.) Oracle SQLJ complies with ANSI standard X3H2-98-320.

SQLJ consists of a translator (a precompiler that supports standard SQLJ programming syntax) and a runtime component. After creating your SQLJ source code in a .sqlj file, you process it with the translator, which translates your SQLJ source code to standard Java source code, with SQL operations converted to calls to the SQLJ runtime. In the Oracle SQLJ implementation, the translator then invokes a Java compiler to compile the Java source. When your Oracle SQLJ application runs, the SQLJ runtime calls JDBC to communicate with the database.

JDBC Code vs. SQLJ Code

The following is an example of a simple operation, first in JDBC code and then SQLJ code.

JDBC:


// (Presume you already have a JDBC Connection object conn)
// Define Java variables
String name;
int id=37115;
float salary=20000;
// Set up JDBC prepared statement.
PreparedStatement pstmt = conn.prepareStatement
   ("select ename from emp where empno=? and sal>?");
pstmt.setInt(1, id);
pstmt.setFloat(2, salary);
// Execute query; retrieve name and assign it to Java variable.
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
   name=rs.getString(1);
   System.out.println("Name is: " + name);
}
// Close result set and statement objects.
rs.close()
pstmt.close();

The first three lines define the Java variables name, id, and salary. The next line then defines something known as a prepared statement (this presumes you have already established a connection to the database so that you can use the prepareStatement() method of the connection object). You can use a prepared statement whenever values within the SQL statement must be dynamically set (you can use the same prepared statement repeatedly with different variable values). The question marks in the prepared statement are placeholders for Java variables and are given values in the pstmt.setInt() and pstmt.setFloat() lines of code. The first "?" is set to the int variable id (with a value of 37115). The second "?" is set to the float variable salary (with a value of 20000). Then the query is executed and returns the data into a JDBC result set object. (You can use result sets to gather query data.) Finally, the data of interest (the name) is retrieved from the result set and printed. A result set usually contains multiple rows of data, although this example has only one row.

Now, by comparison, here is some SQLJ code that performs the same task. Note that all SQLJ statements, both declarations and executable statements, start with the #sql token.

SQLJ:


String name;
int id=37115;
float salary=20000;
#sql {select ename into :name from emp where empno=:id and sal>:salary);
System.out.println("Name is: " + name);

SQLJ, in addition to allowing SQL statements to be directly embedded in Java code, supports Java host expressions (also known as bind expressions) to be used directly in the SQL statements. In the simplest case, a host expression is a simple variable as in this example, but more complex expressions are allowed as well. Each host expression is preceded by ":" (colon); this example uses Java host expressions name, id, and salary. In SQLJ, because of its host expression support, you do not need a result set or equivalent when you are returning only a single row of data.

SQLJ also allows you to catch errors in your SQL statements before runtime. JDBC code, being pure Java, is compiled directly. The compiler has no knowledge of SQL, however, so it is unaware of any SQL errors. By contrast, when you translate SQLJ code, the translator analyzes the embedded SQL statements semantically and syntactically, catching SQL errors during development instead of allowing an end-user to catch them when running the application.

Complete SQLJ Example

This section presents a complete example of a simple SQLJ program:


import java.sql.*;
import sqlj.runtime.ref.DefaultContext;
import oracle.sqlj.runtime.Oracle;
#sql iterator MyIter (String ename, int empno, float sal);

public class MyExample 
{
   public static void main (String args[]) throws SQLException 
   {
      Oracle.connect
         ("jdbc:oracle:thin:@oow11:5521:sol2", "scott", "tiger");

      #sql { insert into emp (ename, empno, sal)
         values ('SALMAN', 32, 20000) };
      MyIter iter;

      #sql iter={ select ename, empno, sal from emp };
      while (iter.next()) {
         System.out.println
            (iter.ename()+" "+iter.empno()+" "+iter.sal());
      }
   }
}

The first SQLJ statement is an iterator declaration. SQLJ uses a strongly typed version of JDBC result sets, known as iterators. The main difference between the two is that an iterator has a specific number of columns of specific datatypes. You must define your iterator types beforehand, as in this example:


#sql iterator MyIter (String ename, int empno, float sal);

This declaration results in SQLJ creating an iterator class MyIter. Iterators of type MyIter can store results whose first column maps to a Java String, whose second column maps to a Java int, and whose third column maps to a Java float. This definition also names the three columns--ename, empno, and sal, respectively--to match the table column names in the database. (MyIter is a named iterator. See Chapter 3 of the Oracle8i SQLJ Developer's Guide and Reference to learn about positional iterators, which do not require column names.)

The first statement of the main method is:


   Oracle.connect
("jdbc:oracle:thin:@oow11:5521:sol2","scott", "tiger");

Oracle SQLJ furnishes the Oracle class, and its connect() method accomplishes three important things: 1) it registers the Oracle JDBC drivers SQLJ uses to access the database; 2) it opens a database connection for the specified schema (user scott, password tiger) at the specified URL (host oow11, port 5521, SID so12, "thin" JDBC driver); 3) it establishes this connection as the default connection for your SQLJ statements. Although each JDBC statement must explicitly specify a connection object, a SQLJ statement can either implicitly use a default connection or optionally specify a different connection.

The first SQLJ executable statement is the following, which inserts a row into the emp table:


#sql {insert into emp (ename, empno, sal) values ('SALMAN', 32, 20000)};

The next SQLJ statement, following the declaration of an iterator variable, executes a query. This instantiates and populates the iterator:


MyIter iter;
#sql iter={select ename, empno, sal from emp};

And once the iterator is populated, you access it as follows:


while (iter.next()){
   System.out.println(iter.ename()+" "+iter.empno()+" "+iter.sal());
}

The next() method is common to all iterators and plays the same role as the next() method of a JDBC result set, returning true and moving to the next row of data if any rows remain. You access the data in each row by calling iterator accessor methods whose names match the column names (this is a characteristic of all named iterators). In this example, you access the data using the methods ename(), empno(), and sal().

SQLJ Strong Typing Paradigm

The preceding example and discussion of iterators points out a key advantage of SQLJ. Its use of strong typing, such as iterators instead of result sets, allows your SQL instructions to be checked against the database during translation. For example, SQLJ can optionally connect to a database and check your iterators against the database tables that will be queried. The translator will verify that they match, allowing you to catch SQL errors during translation that would otherwise not be caught until a user runs your application. Furthermore, if changes are subsequently made to the schema, you can determine if this affects the application simply by re-running the translator.

Translating a SQLJ Program

Integrated development environments such as Oracle JDeveloper, a Windows-based visual development environment for Java programming, can translate, compile, and customize your SQLJ program for you as you build it. If you are not using an IDE, then you can use the front-end SQLJ utility, sqlj. You can specify many options when you run the sqlj utility, but in the simplest case you can run it as follows:


%sqlj MyExample.sqlj

While translating a .sqlj file, the SQLJ translator checks the syntax and semantics of your SQL operations. Additionally, you can enable online checking to check your operations against the database. In doing this, you must specify an example database schema in your translator option settings. It is not necessary for the schema to have identical data to the one the program will eventually run against, but the tables in it should have columns with corresponding names and datatypes. Use the translator user option to enable online checking, and specify the username, password, and URL of your exemplar schema, as in the following example:


%sqlj -user=scott/tiger@jdbc:oracle:thin:@oow11:5521:sol2
MyExample.sqlj

Running a SQLJ Program in the Server vs. Running on a Client

Many SQLJ applications run on a client; however, SQLJ, with its simple syntax, offers particular advantage in programming stored procedures (which are usually SQL-intensive) to run in the server.

There is almost no difference between coding for a client-side SQLJ program and a server-side SQLJ program. The SQLJ runtime packages are automatically available on the server, and there are just the following few coding considerations (which Chapter 11 of the Oracle8i SQLJ Developer's Guide and Reference further discusses):

To run a SQLJ program in the server, presuming you develop the code on a client (as is usually the case), you have two options:

In either case, you can use the Oracle loadjava utility to load the file or files to the server. Chapter 11 of the Oracle8i SQLJ Developer's Guide and Reference also discusses this.

Converting a Client Application to Run in the Server

The steps in converting an existing SQLJ client-side application to run in the server are fairly simple and straightforward. Presume this is an application that has already been translated on the client:

  1. Create a .jar file for your application components.

  2. Use the loadjava utility to load the .jar file to the server.

  3. Create a SQL wrapper in the server for your application. For example, to run the preceding MyExample application in the server:

    
          create or replace procedure SQLJ_MYEXAMPLE as language java
          name `MyExample.main(java.lang.String[])';
    
    

You can then execute SQLJ_MYEXAMPLE as with any other stored procedure.

Interacting with PL/SQL

All of the Oracle JDBC drivers communicate seamlessly with Oracle SQL and PL/SQL, and it is important to note that SQLJ interoperates with PL/SQL. You can start using SQLJ without having to rewrite any PL/SQL stored procedures. Oracle SQLJ includes syntax for calling PL/SQL stored procedures and also allows PL/SQL anonymous blocks to be embedded in SQLJ executable statements, just as with SQL operations.

Distributed Objects Using CORBA and EJB

Java makes a terrific, simple but general purpose, language for writing stored procedures. JDBC and SQLJ supply the glue that allows Java to access SQL data. The glue comes in the form of Java language support for SQL operations and concepts, variable bindings between Java and SQL types, and supporting classes that map Java classes to SQL types. You can write portable Java code that can execute on a client or a server without change. With JDBC and SQLJ, the dividing line between client and server is usually obvious--SQL operations happen in the server, and application program logic resides in the client.

Normally, in a program whose logic is distributed, the architecture of choice has three tiers--the client, the middle tier, and the database server. The client tier is typically limited to display of information provided by the middle tier. The middle tier performs the business or application logic, accessing data that resides on the third tier, the database server. Oracle8i removes the need for a physical middle tier for many applications--those that require fast access to the database. Oracle8i still maintains a three-tier logical architecture, but by combining the middle tier and the database server, the physical architecture is two-tier. The flexibility inherent in this architecture is ideally suited to Internet applications where the client presents information in a Web browser, interacting with servers across the network. Those servers, in turn, can be federated and cooperate in their own client-server interactions to provide information to Web-based clients in an intranet or Internet application.

As you write more complex Java programs, you will discover opportunities to gain performance and scalability by controlling the location at which program logic executes. You will want to minimize network traffic and maximize locality of reference to SQL data. JDBC and SQLJ, particularly with the new JServer support for Java in Oracle8i, furnish ways to accomplish these goals. However, as you tend to leverage the object model in your Java application, a more significant portion of time is spent in Java execution, as opposed to SQL data access and manipulation. It becomes more important to understand and specify where Java objects reside and execute in an Internet application. Now you have become a candidate for moving into the world of CORBA and Enterprise JavaBeans.

A key feature of CORBA and EJB is the use of standards to specify components. CORBA uses Interface Definition Language (IDL) to specify, in a language-independent manner, how to access and use a group of objects known as a component. Enterprise JavaBeans extends this concept by relying on Java class definitions to specify the interface to a component and an RMI-style declarative deployment descriptor to define how the component is treated in a transactional, secure application. CORBA and EJB are complementary; the JServer implementation of the Enterprise JavaBeans 1.0 specification builds on the underlying support and services of CORBA.

You can access components through a name service, which forms a tree, similar to a file system, where you can store objects by name. The Java Naming and Directory Interface (JNDI) package provides a unified interface to name services. Part of JNDI provides a platform-independent abstraction for accessing a file system--something that is very platform dependent. When you put a CORBA object into the namespace, you are publishing it. Although this may seem analogous to publishing entry points (or Java static methods) through call specifications, as discussed with Java stored procedures, it is actually much more powerful. With EJB, you are publishing a component whose interface is completely specified, including the manner in which a transaction treats it and the security restrictions on its use. With Java stored procedures and call specifications, you are limited to SQL data types as arguments and as return values (or the limited set of Java classes that represent these SQL data types, to be more exact). With CORBA and EJB, you operate purely in an object-oriented, message-based world. Methods take objects as arguments and return objects. These objects maintain object identity during your session. This means that the communication protocol between a client and server is object-oriented and seamlessly integrated into the Java language.

Oracle 8i interacts with each client as if it had its own virtual machine running in the server. Because of this architecture, there is no single ORB in the JServer servicing multiple client requests. Instead, JServer leverages off of Oracle8i's Multithreaded Server (MTS) architecture, providing an ORB per session. Unlike a session in which the client communicates through SQL*Net, you access CORBA and EJB sessions through IIOP, and they are capable of servicing multiple client connections. Although scalable applications will generally provide one session per client-server interaction, the ability to serve multiple clients greatly extends the flexibility of the session in comparison to SQL*Net, allowing callbacks and loopbacks in your distributed communications. Such capabilities are an important requirement of CORBA and general Java programs.

If you are a CORBA programmer, then you are familiar with bootstrapping your application. In effect, your client Java code has to obtain handles to objects that actually reside on the server to operate. Those objects are reachable through the name service discussed in the preceding section. The ORB typically supplies the name service, which presupposes that the ORB is running when you attempt to locate the server objects necessary to bootstrap your client application. In the JServer, there is no ORB process per se. Instead, JServer provides an activation service based on CORBA's CosNaming. We provide a JNDI interface to CosNaming so that you use URL-based naming to refer to and activate CORBA objects in a session. This namespace incorporates the idea of a session directly in the URL, allowing the client to easily manipulate multiple sessions. In effect, all bootstrapping is performed by establishing a session with the JServer and using objects always reachable from the Oracle8i database that the standard Java JNDI and CORBA CosNaming make visible to you. You need not mess with flat-file-based Inter-ORB References (IORs) as you might be used to with most CORBA programming.

EJB makes component-based programming even easier than CORBA. An EJB programmer simply writes business logic and the interfaces to the component, and a deployment tool, deployejb, takes care of the rest. No knowledge of IDL, how transactions are implemented, nor how to implement security is necessary. This portable Java-based server framework provides a fast, scalable and easy solution to Java-based, three-tier applications.

Creating and Deploying Enterprise JavaBeans

CORBA and EJB application development are complicated topics that the Oracle8i Enterprise JavaBeans and CORBA Developer's Guide thoroughly covers. Here, we want to take just enough of a look at EJB for you to become familiar with the pieces and process. We will follow the example in the Oracle8i Enterprise JavaBeans and CORBA Developer's Guide of using an EmployeeBean to look up an employee record in the Oracle RDBMS. The steps are:

  1. Create the home interface. The home interface will reside on the server, enabling you to create instances of your EJB in the server. A home interface is a Java interface you write that extends EJBHome. The home interface is the only object normally published in the namespace visible to clients, through JNDI.

  2. Create the remote interface. The remote interface specifies the methods you implement in the EJB, such as instance methods you can invoke from a client. A remote interface is a Java interface you write that extends EJBObject. As an interface, you use it only to specify the methods that you later implement in the bean.

  3. Implement the bean class and the methods the remote interface defines. In other words, you must actually implement the behavior of your bean class. In the EmployeeBean example, there is only one method, getEmployee(). You write a bean, in this case EmployeeBean, by creating a class that implements the SessionBean interface. (JServer does not currently implement the optional EJB 1.0 entity bean concept, primarily because of scalability concerns). The SessionBean interface requires a few additional methods, but these are typically trivial. Your main job is to implement the application logic of the bean.

  4. Create the deployment descriptor. The deployment descriptor specifies attributes of the bean, including its transactional properties and security treatment. You specify the attributes, and the deployejb tool ensures that the server enforces them.

  5. Deploy the EJB. When you deploy the EJB, the deployejb tool places your home interface and the EJB on the server, publishing the home interface in the namespace. It generates the necessary Java code on the server side to handle transactions and security that you specify in your deployment descriptor. Finally, it generates and returns the stub interfaces that provide the client access to the remote functionality of the bean.

Using an EJB

Once you create and deploy an EJB, you will want to use it from a client program. You can use EJBs between servers in n-tier applications also, in which case the client for one server may also be the server for other clients. In your client code, you must perform the following steps:

  1. Locate the home interface object that resides on the server. You will locate the object using Java-standard JNDI lookup facilities.

  2. Authenticate the client to the server. EJB and CORBA clients use database sessions just as with any other Oracle client. To initiate a session, you must let the server know you are a valid user. You can use several different approaches to accomplish authentication in a secure manner.

  3. Activate an instance of the bean. Because the object you locate with JNDI is the home interface, you will use one of its create() methods to return an activated instance of the EJB.

  4. Invoke methods on the bean. When you invoke a method on the bean, the method is actually executed in the server, and the appropriate parameter and return objects are transparently transported (by copy) across the underlying IIOP connection. All objects the EJBs return must be serializable--they must implement java.io.Serializable.

The Oracle8i Enterprise JavaBeans and CORBA Developer's Guide discusses the details of these steps. Java IDEs, as with Oracle's JDeveloper, can further automate and simplify the deployment and descriptor process.

Session Shell

The session shell provides a shell-like interface to the server. This shell allows users to manipulate the session namespace with familiar Unix commands, such as mkdir, ls, and rm. In addition, the session shell furnishes a convenient way to run Java programs in the server, using the java command. As with the client side java program that executes a client Java virtual machine, the session shell java command takes the name of a class and any arguments the user types in. The session shell calls the static main(String[]) method on the class, running the Java program in the server. System.out and System.err are captured and transparently redirected back to the user's console.

The session shell is a great example of how you can use the CORBA support in the JServer to build Java client applications that directly invoke server-side Java functionality. It illustrates the kind of simplicity and power achievable when you have mastered Java and CORBA/EJB and you use those skills with the scalable JServer platform. Internet standards and Java truly combine to make Oracle8i the Internet computing platform.




Prev

Next
Oracle
Copyright © 1999 Oracle Corporation.

All Rights Reserved.

Library

Product

Contents

Index