Oracle8i Enterprise JavaBeans and CORBA Developer's Guide
Release 2 (8.1.6)

A81356-01

Library

Product

Contents

Index

Prev Next

3
Developing CORBA Applications

This chapter describes how to develop CORBA applications for Oracle8i. CORBA is a powerful distributed application development architecture. Although it is a powerful tool, you can still start to develop useful applications quickly, using Oracle8i CORBA. The emphasis in this chapter is practical, not conceptual. The first few sections of this chapter present the conceptual basis for CORBA application development.

This chapter is based on examples, showing you how to use Oracle8i CORBA by developing examples--from the simple to the slightly more complex. You can expand these examples into full-fledged applications for your enterprise.

This chapter covers the following topics:

Terminology

This section defines some of the basic terms used in this chapter. See also Appendix D, "Abbreviations and Acronyms" for a list of common acronyms used in Java and distributed object computing.

client

A client is an object, an application, or an applet that makes a request of a server object. Remember that a client need not be a Java application running on a workstation or a network computer, nor an applet downloaded by a web browser. A server object can be a client of another server object. "Client" refers to a role in a requestor/server relationship, not to a physical location or a type of computer system.

marshalling

In distributed object computing, marshalling refers to the process by which the ORB passes requests and data between clients and server objects.

object adapter

Each CORBA ORB implements an object adapter (OA), which is the interface between the ORB and the message-passing objects. CORBA 2.0 specifies that a basic object adapter (BOA) must exist, but most of the details of its interface are left up to individual CORBA vendors. Future CORBA standards will require a vendor-neutral portable object adapter (POA). Oracle intends to support a POA in a future release.

request

A request is a method invocation. Other names sometimes used in its stead are method call and message.

server object

A CORBA server object is a Java object activated by the server, typically on a first request from a client.

session

A session always means a database session. Although it is conceptually the same kind of session as that established when a tool such as SQL*Plus connects to Oracle, there are differences in the CORBA case, as follows:

See Chapter 4, "Connections and Security", for more information about sessions.

About CORBA

This section provides a short introduction to CORBA, and should give you some idea of how you typically use CORBA in the Oracle8i server environment. Providing a complete introduction to CORBA is beyond the scope of this Guide. See the references in "For More Information" for suggested further reading. This first section gives a very high-level overview of CORBA itself.

CORBA stands for Common Object Request Broker Architecture. What is common about CORBA is that it integrates ideas from several of the original proposers. CORBA did not just follow the lead of a single large corporation, and it is very deliberately vendor neutral. The CORBA architecture specifies a software component, a broker, that mediates and directs requests to objects that are distributed across a network (or several networks), which might have been written in a different language from that of the requestor, and which might be (and in fact, usually are) running on a completely different hardware architecture from that of the requestor.

You can begin to get an idea of the tremendous advantages of CORBA from the preceding paragraph. CORBA enables your application to tie together components from various sources. Also, and unlike a typical client/server application, a CORBA application is not inherently synchronous. It is not necessarily typical that a CORBA requestor (a client) invokes a method on a server component and waits for a result. Using asynchronous method invocations, event interfaces and callbacks from server object to the client ORB, you can construct elaborate applications that link together many interacting objects and that access one or many data sources and other resources under transactional control. CORBA enables you to go beyond the bounds of the traditional client/server application in many imaginative ways.

CORBA Features

CORBA achieves its flexibility in several ways:

The remainder of this section introduces some of the essential building blocks of an Oracle8i JServer CORBA application. These include:

About the ORB

The object request broker, or ORB, is the fundamental part of a CORBA implementation. The ORB makes it possible for a client to send messages to a server, and the server to return values to the client. The ORB handles all communication between a client and a server object.

The JServer ORB is based on code from Inprise's VisiBroker 3.4 for Java. The ORB that executes on the server side has been slightly modified from the VisiBroker code, to accommodate the different Oracle8i object location and activation model. The client-side ORB has been changed very little.

In some CORBA implementations, the application programmer and the server object developer must be aware of the details of how the ORB is activated on the client and the server, and they must include code in their objects to start up the ORBs and activate objects. The Oracle8i ORB, on the other hand, makes these details largely transparent to the application developer. As you will see from the Java code examples later in this chapter and in Appendix A, only in certain circumstances does the developer need to control the ORB directly. These occur, for example, when coding callback mechanisms, or when there is a need to register transient objects with the basic object adapter.

The Interface Definition Language (IDL)

One of the key factors in the success of CORBA is language independence. CORBA objects written in one language can send requests to objects implemented in a different language. Objects implemented in an object-oriented language such as Java or Smalltalk can talk to objects written in C or COBOL, and the converse .

Language independence is achieved through the use of a specification meta-language that defines the interfaces that an object (or a piece of legacy code wrappered to look like an object) presents to the outside world. As in any object-oriented system, a CORBA object can have its own private data and its own private methods. The specification of the public data and methods is the interface that the object presents to the outside world.

IDL is the language that CORBA uses to specify its objects. You do not write procedural code in IDL--its only use is to specify data, methods, and exceptions.

Each CORBA vendor supplies a compiler that translates IDL specifications into language code. Oracle8i JServer uses the idl2java compiler from Inprise (see "Miscellaneous Tools"). The idl2java compiler translates your IDL interface specifications into Java classes, which are compiled by the Java compiler into byte codes to be loaded into the Oracle8i database for execution.


Note:

The idl2java compiler accepts only ASCII characters. Do not use ISO Latin-1 or other non-ASCII NLS characters in IDL files.  


Using IDL

Here is an example of a short IDL file. It is the IDL for the HelloWorld example (see "helloworld" for the complete example):

module hello {
  interface Hello {
    wstring helloWorld();
  };
};

The IDL consists of a module, which contains a group of usually related object interfaces. By default, the IDL compiler uses the module name to name a directory where the IDL compiler puts the Java classes that it generates, and this maps to a Java package.

This module has only a single interface: Hello. The Hello interface defines a single operation: helloWorld, which takes no parameters and returns a wstring (a wide string, which is mapped to a Java String).


Note:

IDL data and exception types, such the wstring shown in the preceding example, are not specified in this guide. Although some of the IDL to Java bindings are listed in this guide (for example see "IDL Types"), CORBA developers should have access to the OMG specifications for complete information about IDL and IDL types. See "For More Information".  


The module and interface names must be valid Java identifiers and also valid file names for your operating system. When naming interfaces and modules, remember that both Java and CORBA objects are portable, and that some operating systems are case sensitive and some are not, so be sure to keep names distinct in your project.

Nested Modules

You can nest modules. For example, an IDL file that specifies

module org {
  module omg {
     module CORBA {
       ...
     };
    ...
  };
   ...
};

would map to the Java package hierarchy package org.omg.CORBA.

Running the IDL Compiler

Assume that the HelloWorld IDL is saved in a file called hello.idl. When you run idl2java to compile the hello module, eight Java class files are generated and are put in a subdirectory named hello in the same directory as the IDL file:

% idl2java hello.idl
Traversing hello.idl
Creating: hello/Hello.java
Creating: hello/HelloHolder.java
Creating: hello/HelloHelper.java
Creating: hello/_st_Hello.java
Creating: hello/_HelloImplBase.java
Creating: hello/HelloOperations.java
Creating: hello/_tie_Hello.java
Creating: hello/_example_Hello.java

The ORB uses eight Java classes to invoke a remote object, pass and return parameters, and perform various other things. You can control the files generated, where they are put, and other aspects of IDL compiling (such as whether the IDL compiler generates comments in the Java files). See the complete description of the idl2java compiler in Chapter 6, "Tools".

Each of the eight files the compiler generates is described briefly below.

Hello
 

This is the interface file that specifies in Java what the interface to a Hello object looks like. In this case, the interface is:

package hello;
public interface Hello extends org.omg.CORBA.Object {
  public java.lang.String helloWorld();
}

Note that because the file is put in a hello directory, it takes the package spec from that name. All CORBA basic interface classes subclass, directly or indirectly, org.omg.CORBA.Object.

The server object developer must implement the methods in the interface. Typical of the examples in this guide is that the implementation class for an interface named hello.java would be named helloImpl, but this naming convention is not a requirement.  

HelloHolder
 

The application uses the holder class when parameters in the interface operation are of types out or inout. Because the ORB passes Java parameters by value, special holder classes are necessary to provide for parameter return values.  

HelloHelper
 

The helper classes contain methods that read and write the object to a stream, and cast the object to and from the type of the base class. For example, the helper class has a narrow() method that is used to cast an object to the appropriate type, as in the following code:

    LoginServer lserver = LoginServerHelper.narrow
          (orb.string_to_object (loginIOR));

Note that when you get an object reference using the JNDI InitialContext lookup() method, you do not have to call the helper narrow() method. The ORB calls it automatically for you.  

_st_Hello
 

The generated files that have _st_ prefixed to the interface name are the stub files or client proxy objects. (_st_ is a VisiBroker-specific prefix.)

These classes are installed on the client that calls the remote object (the hello object, in this example). In effect, when a client calls a method on the remote object, it is really calling into the stub, which then performs the operations necessary to perform a remote method invocation. For example, it must marshall parameter data for transport to the remote host.  

_HelloImplBase
 

Generated source files of the form _<interfaceName>ImplBase are the skeleton files. A skeleton file is installed on the server and communicates with the stub file on the client, in that it receives the message on the ORB from the client and upcalls to the server. The skeleton file also returns parameters and return values to the client.

In earlier CORBA implementations, the skeleton files were named _sk_<interfaceName>, but this is now deprecated.  

HelloOperations
_tie_Hello
 

The server uses these two classes for Tie implementations of server objects. See "Using the CORBA Tie Mechanism" for information about Tie classes.  

_example_Hello
 

The _example_<interfaceName> class gives you an example of how you should implement the interface on the server. It provides the framework for the implementation code, leaving just the method implementation body blank.

You can copy the example code to the directory where you will implement the Hello server object, rename it following your naming conventions (HelloImpl.java is used in the examples in this Guide), and just add the Java code to implement the methods.  

IDL Interface Body

An IDL interface body contains the following kinds of declarations:

types  

Type definitions.  

exceptions  

Exception structures that the interface exports.  

attributes  

Any associated attributes exported by the interface.  

operations  

Operations are the methods that the interface supports.  

IDL Types

This section gives a brief description of IDL datatypes and their mapping to Java datatypes. For more information about IDL types not covered here, see the CORBA specifications and the books cited in "For More Information".

Basic Types

Mapping between IDL basic types and Java primitive types is straightforward. Table 3-1 shows the mappings, as well as possible CORBA exceptions that can be raised on conversion.

Table 3-1 IDL to Java Datatype Mappings
CORBA IDL Datatype  Java Datatype  Exception 

boolean  

boolean  

 

char  

char  

CORBA::DATA_CONVERSION  

wchar  

char  

 

octet  

byte  

 

string  

java.lang.String  

CORBA::MARSHAL  

 

 

CORBA::DATA_CONVERSION  

wstring  

java.lang.String  

CORBA::MARSHAL  

short  

short  

 

unsigned short  

short  

 

long  

int  

 

unsigned long  

int  

 

long long  

long  

 

unsigned long long  

long  

 

float  

float  

 

double  

double  

 

The IDL character type char is an 8-bit type, representing an ISO Latin-1 character that maps to the Java char type, which is a 16-bit unsigned element representing a Unicode character. On parameter marshalling, if a Java char cannot be mapped to an IDL char, a CORBA DATA_CONVERSION exception is thrown.

The IDL string type contains IDL chars. On conversion between Java String, and IDL string, a CORBA DATA_CONVERSION can be thrown. Conversions between Java strings and bounded IDL string and wstring can throw a CORBA MARSHALS exception if the Java String is too large to fit in the IDL string.

Constructed Types

Perhaps the most useful IDL constructed (aggregate) type for the Java developer is the struct. The IDL compiler converts IDL structs to Java classes. For example, the IDL specification:

module employee {
  struct EmployeeInfo {
    long empno;
    wstring ename;
    double sal; 
  };
  ...

causes the IDL compiler to generate a separate Java source file for an EmployeeInfo class. It looks like this:

package employee;
final public class EmployeeInfo {
  public int empno;
  public java.lang.String ename;
  public double sal;
  public EmployeeInfo() {
  }
  public EmployeeInfo(
    int empno,
    java.lang.String ename,
    double sal
  ) {
    this.empno = empno;
    this.ename = ename;
    this.sal = sal;
  }
 ...

The class contains a public constructor with parameters for each of the fields in the struct. The field values are saved in instance variables when the object is constructed. Typically, these are passed by value to CORBA objects.

Collections

The two types of ordered collections in CORBA are sequences and arrays. An IDL sequence maps to a Java array with the same name. An IDL array is a multidimensional aggregate whose size in each dimension must be established at compile time.

The ORB throws a CORBA MARSHAL exception at runtime if sequence or array bounds are exceeded when Java data is converted to sequences or arrays.

IDL also generates a holder class for a sequence. The holder class name is the sequence's mapped Java class name with Holder appended to it.

The following IDL code shows how you can use a sequence of structs to represent information about employees within a department:

module employee {
  struct EmployeeInfo {
    long empno;
    wstring ename;
    double sal; 
  };

  typedef sequence <EmployeeInfo> employeeInfos;

  struct DepartmentInfo {
    long deptno;
    wstring dname;
    wstring loc;
    EmployeeInfos employees;
  };

The Java class code that the IDL compiler generates for the DepartmentInfo class is:

package employee;
final public class DepartmentInfo {
  public int deptno;
  public java.lang.String dname;
  public java.lang.String loc;
  public employee.EmployeeInfo[] employees;
  public DepartmentInfo() {
  }
  public DepartmentInfo(
    int deptno,
    java.lang.String dname,
    java.lang.String loc,
    employee.EmployeeInfo[] employees
  ) {
    this.deptno = deptno;
    this.dname = dname;
    this.loc = loc;
    this.employees = employees;
  }

Notice that the sequence employeeInfos is generated as a Java array EmployeeInfo[].

Specify an array in IDL as follows:

const long ArrayBound = 12;
typedef long larray[ArrayBound];

The IDL compiler generates this as:

public int[] larray;

When you use IDL constructed and aggregate types in your application, you must make sure to compile the generated .java files and load them into the Oracle8i database when the class is a server object. You should scan the generated .java files, and make sure that all required files are compiled and loaded. Study the Makefile (UNIX) or the makeit.bat batch file (Windows NT) of CORBA examples that define these types to see how the set of IDL-generated classes is compiled and loaded into the data server. A good example is "lookup".

Exceptions

You can create new user exception classes in IDL with the exception key word. For example:

  exception SQLError {
    wstring message;
  };

The IDL can declare that operations raise user-defined exceptions. For example:

interface employee {
  attribute name;
  exception invalidID {
    wstring reason;
  };
  ...
  wstring getEmp(long ID)
    raises(invalidID);
  };
};

CORBA System Exceptions

Mapping between OMG CORBA system exceptions and their Java form is also quite straightforward. These mappings are shown in Table 3-2.

Table 3-2 CORBA and Java Exceptions
OMG CORBA Exception  Java Exception 

CORBA::PERSIST_STORE  

org.omg.CORBA.PERSIST_STORE  

CORBA::BAD_INV_ORDER  

org.omg.CORBA.BAD_INV_ORDER  

CORBA::TRANSIENT  

org.omg.CORBA.TRANSIENT  

CORBA::FREE_MEM  

org.omg.CORBA.FREE_MEM  

CORBA::INV_IDENT  

org.omg.CORBA.INV_IDENT  

CORBA::INV_FLAG  

org.omg.CORBA.INV_FLAG  

CORBA::INTF_REPOS  

org.omg.CORBA.INTF_REPOS  

CORBA::BAD_CONTEXT  

org.omg.CORBA.BAD_CONTEXT  

CORBA::OBJ_ADAPTER  

org.omg.CORBA.OBJ_ADAPTER  

CORBA::DATA_CONVERSION  

org.omg.CORBA.DATA_CONVERSION  

CORBA::OBJECT_NOT_EXIST  

org.omg.CORBA.OBJECT_NOT_EXIST  

CORBA::TRANSACTIONREQUIRED  

org.omg.CORBA.TRANSACTIONREQUIRED  

CORBA::TRANSACTIONROLLEDBACK  

org.omg.CORBA.TRANSACTIONROLLEDBACK  

CORBA::INVALIDTRANSACTION  

org.omg.CORBA.INVALIDTRANSACTION  

Getting by Without IDL

The Oracle8i JVM development environment offers the Inprise Caffeine tools, which enable development of pure Java distributed applications that follow the CORBA model. You can write your interface specifications in Java and use the java2iiop tool to generate CORBA-compatible Java stubs and skeletons.

Developers can also use the java2idl tool to code in pure Java, yet still have IDL available that can be shipped to customers who are using a CORBA server that does not support Java. This tool generates IDL from Java interface specifications. See Chapter 6, "Tools", for more information about java2iiop and java2idl.

A First CORBA Application

This section introduces the JServer CORBA application development process. It tells you how to write a simple but useful program that runs on a client system, connects to Oracle using IIOP, and invokes a method on a CORBA server object that is activated and runs inside an Oracle8i Java VM.

This section addresses the purely mechanical aspects of the development process. See "For More Information" for references to documents on CORBA design.

The CORBA application development process has seven phases:

  1. Design and write the object interfaces.

  2. Generate stubs and skeletons, and other required support classes.

  3. Write the server object implementations.

  4. Use the client-side Java compiler to compile both the Java code that you have written, and the Java classes that were generated by the IDL compiler. Generate a JAR file to contain the classes and any other resource files that are needed.

  5. Publish a name for the directly-accessible objects with the CosNaming service, so you can access them from the client program.

  6. Write the client side of the application. This is the code that will run outside of the Oracle8i data server, on a workstation or PC.

  7. Compile the client code using the JDK Java compiler.

  8. Load the compiled classes into the Oracle8i database, using the loadjava tool and specifying the JAR file as its argument. Make sure to include all generated classes, such as stubs and skeletons. (Stubs are required in the server when the server object acts as a client to another CORBA object.)

The remainder of this section describes these steps in more detail, with IDL and Java code examples to illustrate the coding steps.

The first sample application asks the user for an employee number in the EMP table and returns the employee's last name and current salary, or throws an exception if there is no employee in the database with that ID number.

Writing the IDL Code

The application requires only a single server-side object: some code that takes an ID number and queries the database for the other information about the employee.

The interface requires three things:

The example defines an operation called query to get the information, uses an IDL struct to return the information, and defines an exception called SQLError to signal that no employee was found. Here is the IDL code:

module employee {

  struct EmployeeInfo {
    wstring name;
    long number;
    double salary; 
  };

  exception SQLError {
    wstring message;
  };

  interface Employee {
    EmployeeInfo getEmployee (in long ID) raises (SQLError);
  };
};

This code specifies the three things listed above: a struct named EmployeeInfo, an operation or method named getEmployee(), and the SQLError exception.

Generate Stubs and Skeletons

Use the idl2java compiler to compile the interface description. Because there is no use of the Tie mechanism in this example, you can invoke the compiler with the -no_tie option. This means that two fewer classes are generated. The compiler generates the interface, helper, and holder classes for the three objects in the IDL file, as well as a stub and skeleton class for the Employee interface. (The 12th class is the example for the interface. See "Using IDL" for more information about these classes.)

Compile the IDL as follows:

% idl2java -no_tie -no_comments employee.idl


Note:

This section shows separate for each step of the process. Because developing a CORBA application includes many compilation, loading, and publishing steps, Oracle recommends that if you are working in a command-line oriented environment, you always use a makefile or a batch file to control the process. Or, you can use IDE products such as Oracle's JDeveloper to control the process.

Study the make or batch files that come with the CORBA programs on the CD for good examples.  


Write the Server Object Implementation

For this example, you must implement the Employee interface. The _example_Employee.java file that the IDL compiler generates can provide a basis for the implementation. Here is the complete code that implements the interface:

package employeeServer;

import employee.*;
import java.sql.*;

public class EmployeeImpl extends _EmployeeImplBase {

  public EmployeeImpl() {
  }

  public EmployeeInfo getEmployee (int ID) throws SQLError {
    try {
      Connection conn =
          new oracle.jdbc.driver.OracleDriver().defaultConnection ();
      PreparedStatement ps =
          conn.prepareStatement ("select ename, sal from emp where empno = ?");
      try {
        ps.setInt (1, ID);
        ResultSet rset = ps.executeQuery ();
        if (!rset.next ())
          throw new SQLError ("no employee with ID " + ID);
        return new EmployeeInfo (rset.getString (1), ID, rset.getFloat (2));
      } finally {
        ps.close ();
      }
    } catch (SQLException e) {
      throw new SQLError (e.getMessage ());
    }
  }
}

This code uses the JDBC API to perform the query. Notice the use of a prepared statement to accommodate the variable in the WHERE clause of the query. See the Oracle8i JDBC Developer's Guide and Reference for more about Oracle8i JDBC. Also notice that when a JDBC SQLException is caught, the IDL-defined SQLError is thrown back to the client.

Write the Client Code

To access the server object you must be able to refer to it by name. In step 7 of this process, you publish the server object in the Oracle8i database. The client code looks up the published name, and activates the server object as a by-product of the look up. There are a number of other operations that go on when code such as that listed below looks up a published object. For example, the ORB on the server side is started and the client is authenticated using the environment properties supplied when the initial context object is created. See "IIOP Security".

After getting parameters such as the name of the object to look up, an IIOP service name, and some authentication information such as the database username and password, the client code performs the following four steps:

  1. Instantiates and populates a JNDI InitialContext object with the required connect properties. See "About JNDI".

  2. Invokes the lookup() method on the initial context, with a URL as a parameter that specifies the service name and the name of the object to be found. lookup() returns an object reference to the Employee CORBA server object. See "Looking Up an Object" for more information.

  3. Using the object reference returned by the lookup() method invokes the getEmployee() method on the object in the server. This method returns an EmployeeInfo class (derived from the IDL EmployeeInfo struct). For simplicity, an employee ID number is hard-coded as a parameter of this method invocation.

  4. Prints the values returned by getEmployee() in the EmployeeInfo class.

    import employee.*;
    import oracle.aurora.jndi.sess_iiop.ServiceCtx;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import java.util.Hashtable;
    
    public class Client {
      public static void main (String[] args) throws Exception {
        String serviceURL = "sess_iiop://localhost:2481:ORCL";
        String objectName = "/test/myEmployee";
     
    // Step 1:
        Hashtable env = new Hashtable ();
        env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");
        env.put (Context.SECURITY_PRINCIPAL, "SCOTT");
        env.put (Context.SECURITY_CREDENTIALS, "TIGER");
        env.put (Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);
        Context ic = new InitialContext (env);
    
    // Step 2:
        Employee employee = (Employee)ic.lookup (serviceURL + objectName);
    
    // Step 3 (using SCOTT's employee ID number):
        EmployeeInfo info = employee.getEmployee (7788);
    
    // Step 4:
        System.out.println (info.name + " " + info.number + " " + info.salary);
      }
    }
    
    

When the client code runs, it should print the line

SCOTT 7788 3000.0

on the client system console.

Compiling the Java Source

You run the client-side Java byte code compiler to compile all the Java source that you have created, including the client and server object implementation that you wrote, as well as the Java sources for the classes that were generated by the IDL compiler.

For the preceding example, you must compile the following files:

Other generated Java files are compiled following the dependencies that the Java compiler uses.

Oracle8i JServer supports the Java JDK compiler, release 1.1.6. You might be able to use other Java compilers, such as a compiler incorporated in an IDE, but only JDK 1.1.6 is supported for this release.

Load the Classes into the Database

CORBA server objects, such as the EmployeeImpl object created for this example, execute inside the Oracle8i database server. You must load them into the server so that they can be activated by the ORB as required. You must also load all dependent classes, such as IDL-generated Holder and Helper classes, and classes the server object uses, such as the EmployeeInfo class of this example.

Use the loadjava tool to load each of the server classes into the Oracle8i database. For the example in this section, issue the loadjava command in the following way:

% loadjava -oracleresolver -resolve -user scott/tiger 
   employee/Employee.class employee/EmployeeHolder.class 
   employee/EmployeeHelper.class employee/EmployeeInfo.class 
   employee/EmployeeInfoHolder.class employee/EmployeeInfoHelper.class 
   employee/SQLError.class employee/SQLErrorHolder.class 
   employee/SQLErrorHelper.class employee/_st_Employee.class 
   employee/_EmployeeImplBase.class employeeServer/EmployeeImpl.class

You do not load any client implementation classes or any other classes not used on the server side.

It is sometimes more convenient to combine the server classes into a JAR file, and simply use that file as the argument to the loadjava command. In this example, you could issue the command:

% jar -cf0 myJar.jar employee/Employee.class employee/EmployeeHolder.class \
   employee/EmployeeHelper.class employee/EmployeeInfo.class \
   employee/EmployeeInfoHolder.class employee/EmployeeInfoHelper.class \
   employee/SQLError.class employee/SQLErrorHolder.class \
   employee/SQLErrorHelper.class employee/_st_Employee.class \
   employee/_EmployeeImplBase.class employeeServer/EmployeeImpl.class

and then give the loadjava command as simply:

% loadjava -oracleresolver -resolve -user scott/tiger myJar.jar

Publish the Object Name

The final step in preparing the application is to publish the name of the CORBA server object implementation in the Oracle8i database. See "The Name Space" for information about publishing and published objects.

For the example in this section, you can publish the server object using the publish command as follows:

% publish -republish -user scott -password tiger -schema scott 
    -service sess_iiop://localhost:2481:ORCL 
    /test/myEmployee employeeServer.EmployeeImpl employee.EmployeeHelper

This command specifies the following:

See "publish" for more information about the publish command and its arguments.

Run the Example

To run this example, execute the client class using the client-side Java VM. For this example, you must set the CLASSPATH for the java command to include:

You can locate these libraries in the lib directory under the Oracle home location in your installation.

The following invocation of the JDK java command runs this example. The UNIX shell variable ORACLE_HOME might be represented as %ORACLE_HOME% on Windows NT and that JDK_HOME is the installation location of the Java Development Kit (JDK), version 1.1.6:

% java -classpath 
.:$(ORACLE_HOME)/lib/aurora_client.jar:$(ORACLE_HOME)/jdbc/lib/classes111.zip:
$(ORACLE_HOME)/sqlj/lib/translator.zip:$(ORACLE_HOME)/lib/vbjorb.jar:
$(ORACLE_HOME)/lib/vbjapp.jar:$(JDK_HOME)/lib/classes.zip Client 
sess_iiop://localhost:2481:ORCL /test/myEmployee scott tiger 

This example assumes that you invoke the client with four arguments on the command line:

From the java command you can see why it is almost always better to use a makefile or a batch file to build CORBA applications.

Locating Objects

One of the fundamental tasks that a CORBA programmer faces is discovering how to get a reference to a server object. The CORBA specifications permit a great deal of freedom to the implementer in this area.

As you saw in the example in the previous section, the Oracle8i solution is to publish non-transient objects in a Oracle8i database instance, using a CORBA CosNaming service. JServer provides a URL-based JNDI interface to CosNaming, to make it easy for clients written in Java to locate and activate published objects.

The Name Space

The name space in the database looks just like a typical file system. You can examine and manipulate objects in the publishing name space using the session shell tool. (See "sess_sh" for information about the session shell.) There is a root directory, indicated by a forward slash ('/'). The root directory is built to contain three other directories: bin, etc, and test. The /test directory is the place where most objects are published for the example programs in this guide. You can also create new directories under root to hold objects for separate projects, however, you must have access as database user SYS to create new directories under the root.

There is no effective limit to the depth that you can nest directories.


Note:

The initial values in the publishing name space are set up when the JServer product for Oracle8i is installed.  


The /etc directory contains objects the ORB uses. Do not delete objects in the /etc directory. They are owned by SYS; therefore, to delete them, you would have to be connected in the session shell as SYS. The objects contained in /etc are:

deployejb    execute    loadjava    login    transactionFactory 

The entries in the name space are actually represented by objects that are instances of the classes oracle.aurora.AuroraServices.PublishingContext and oracle.aurora.AuroraServices.PublishedObject. A publishing context represents a class that can contain other objects (a directory), and the PublishedObject class is used for the leafs of the tree, that is the object names themselves. These classes are documented in the JavaDoc on the product CD.

Published names for objects are stored in a database table. Each published object also has a set of associated permissions, maintained in a separate table in the system tablespace. Each class or resource file can have a combination (union) of the following permissions:

read

The holder of read permission can list the class or the attributes of the class, such as its name, its helper class, and its owner.

write

The holder of write permission for a context can bind new object names into a context. For an object (a leaf node of the tree), write permission allows the holder to republish the object under a different name.

execute

You must have execute permission to resolve and activate an object represented by a context or published object name.

You can set these permissions when the objects are loaded into the database, and can use the session shell tool to view and change object permissions. See "sess_sh" for information about this tool.

Publishing means registering the object name in the database name service. The steps involved are as follows:

Looking Up an Object

The JNDI lookup() method is the normal way that a client looks up an object whose name is published in the name space. When you invoke the lookup() method, you normally pass it a String parameter that specifies a URL containing the following:

Service Name

The service name specifies a service that an IIOP presentation manages, and it represents a database instance. The format of the service URL is explained in "URL Syntax". Briefly, the service name specifies the following components:

A typical example of a service name is sess_iiop://localhost:2481:ORCL, where sess_iiop is the service, localhost defaults to the host of the local database, 2481 is the default listener port for IIOP connections, and ORCL is the SID.

For more information about the service name, see "URL Syntax".

Object name

The object name specifies the complete path name of the published object that you want to look up. For example: /test/myServer.

See "The JNDI InitialContext Class" for further information about the lookup() method.

Activating ORBs and Server Objects

A CORBA application requires that an ORB be active on both the client system and the system running the server. In looking at the examples shown so far in this chapter, it is not obvious how the ORB is activated, either on the client or the server. This section presents more information about that topic.

Client Side

The client-side ORB is normally initialized as part of the processing that goes on when the client invokes the lookup() method on the JNDI InitialContext object that it instantiates.

If you need to get a reference to the client ORB, use the init() method on the ORB pseudo-object to get it, as shown in this statement:

org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init ();

The init() method invoked on the client with no parameters always returns a reference to the existing client ORB.

Server Side

The presentation that manages IIOP requests starts the ORB on the server when the session is created.

About Object Activation

Objects are activated on demand. When a client looks up an object, the ORB loads the object into memory and caches it. To activate the object, the ORB looks up the class by the fully-qualified class name under which the object was published. The class name is resolved in the schema defined at publication time, rather than the caller's schema. See the description of the command-line tool "publish" for more information.

When the class is located, the ORB creates a new instance of the class, using newInstance(). For this reason, the no-argument constructor of a persistent object class must be public. If the class implements the oracle.aurora.AuroraServices.ActivatableObject interface (as determined by reflection), then the _initializeAuroraObject()message is sent to the instance. (See "Using the CORBA Tie Mechanism" for an example that requires _initializeAuroraObject()).

There is no need for the server implementation to register persistent objects with the object adapter using a boa.obj_is_ready() call--the JServer ORB performs this automatically.

You register transient objects generated by other objects, such as persistent published objects, with the BOA using obj_is_ready(). For an example, see the factory demo in the examples/corba/basic/factory directory of the product CD.

CORBA Interceptors

Visibroker enables you to implement interceptors. The Visibroker documentation provides details for how to create them. When you want server interceptors in Oracle8i, you must create a PUBLIC synonym for the interceptor init class that implements ServiceInit.

Using SQLJ

You can often simplify the implementation of a CORBA server object by using Oracle8i SQLJ to perform static SQL operations. Using SQLJ statements results in less code than the equivalent JDBC calls and makes the implementation easier to understand and debug. This section describes a version of the example first shown in "A First CORBA Application", but uses SQLJ rather than JDBC for the database access. Refer to the Oracle8i SQLJ Developer's Guide and Reference for complete information about SQLJ.

The only code that changes for this SQLJ implementation is in the EmployeeImpl.java file, which implements the Employee object. The SQLJ implementation, which can be called EmployeeImpl.sqlj, is listed below. You can contrast that with the JDBC implementation of the same object in "Write the Server Object Implementation".

package employeeServer;

import employee.*;
import java.sql.*;

public class EmployeeImpl extends _EmployeeImplBase {
  public EmployeeInfo getEmployee (int ID) throws SQLError {
    try {
      String name = null;
      double salary = 0.0;
      #sql { select ename, sal into :name, :salary from emp
             where empno = :ID };
      return new EmployeeInfo (name, empno, (float)salary);
    } catch (SQLException e) {
      throw new SQLError (e.getMessage ());
    }
  }
}

The SQLJ version of this implementation is considerably shorter than the JDBC version. In general, Oracle recommends that you use SQLJ where you have static SQL commands to process, and use JDBC, or a combination of JDBC and SQLJ, in applications where dynamic SQL statements are required.

Running the SQLJ Translator

To compile the EmployeeImpl.sqlj file, you issue the following SQLJ command:

% sqlj -J-classpath 
.:$(ORACLE_HOME)/lib/aurora_client.jar:$(ORACLE_HOME)/jdbc/lib/classes111.zip:
$(ORACLE_HOME)/sqlj/lib/translator.zip:$(ORACLE_HOME)/lib/vbjorb.jar:
$(ORACLE_HOME)/lib/vbjapp.jar:$(JDK_HOME)/lib/classes.zip -ser2class
  employeeServer/EmployeeImpl.sqlj

This command does the following:

The SQLJ translation generates two additional class files:

employeeServer/EmployeeImpl_SJProfile0
employeeServer/EmployeeImpl_SJProfileKeys

which you must also load into the database when you execute the loadjava command.

A Complete SQLJ Example

This example is available in complete form in the examples/corba/basic example directory, complete with a Makefile or Windows NT batch file so you can see how the example is compiled and loaded. See also "sqljimpl".

Migrating from JDK 1.1 to Java 2

Oracle8i JServer updated its ORB implementation to Visibroker 3.4, which is compatible with both JDK 1.1 and Java 2.


Note:

All existing CORBA applications must regenerate their stubs and skeletons to work with 8.1.6. You must use the 8.1.6 tools when regenerating code from an IDL file.  


Sun Microsystems's Java 2 contains an OMG CORBA implementation; JDK 1.1 did not contain an OMG CORBA implementation. Thus, when you imported the Inprise libraries and invoked the CORBA methods, it always invoked the Visibroker implementation. With the implementation being contained in Java 2, if you invoke the CORBA methods without any modifications--as discussed below--you will invoke the Sun Microsystems CORBA implementation, which can cause unexpected results.

The following lists the three methods for initializing the ORB on the client-side and recommendations for bypassing the Sun Microsystems CORBA implementation:

JNDI Lookup

If you are using JNDI on the client to access CORBA objects that reside in the server, no code changes are necessary. However, you must regenerate your CORBA stubs and skeletons.

Aurora ORB Interface

If your client environment uses JDK 1.1, you do not need to change your existing code. However, you must regenerate your stubs and skeletons.

If your client environment has been upgraded to Java 2, you can initialize the ORB through the oracle.aurora.jndi.orb_dep.Orb.init method. This method guarantees that when you initialize the ORB, it will initialize only a single ORB instance. That is, if you use the Java 2 ORB interface, it returns a new ORB instance each time you invoke the init method. Aurora's init method initializes a singleton ORB instance. Each successive call to init returns an object reference to the existing ORB instance.

In addition, the Aurora ORB interface manages the session-based IIOP connection.

oracle.aurora.jndi.orb_dep.Orb Class

There are several init methods, each with a different parameter list. The following describes the syntax and parameters for each init method.


Note:

The returned class for each init method is different. You can safely cast the org.omg.CORBA.ORB class to com.visigenic.vbroker.orb.ORB.  


No Parameters

If you execute the ORB.init method that takes no parameters, it does the following:

Syntax
public com.visigenic.vbroker.orb.ORB init();

Providing ORB Properties

If you execute the ORB.init method that takes the ORB properties as the only parameter, it does the following:

Syntax
public org.omg.CORBA.ORB init(Properties props);

Providing Input Arguments and ORB Properties

If you execute the ORB.init method that takes the ORB properties and ORB command-line arguments, it always creates an ORB instance and returns the reference to you.

Syntax
public org.omg.CORBA.ORB init(String[] args, Properties props);

Parameter   Description  

Properties props  

ORB system properties.  

String[] args  

Arguments that are passed to the ORB instance.  

Example 3-1 Using Aurora ORB init method

The following example shows a client instantiating an ORB using the Aurora Orb class.

// Create the client object and publish it to the orb in the client
// Substitute Aurora's Orb.init for OMG ORB.init call
// old way: org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init ();
com.visigenic.vbroker.orb.ORB orb = oracle.aurora.jndi.orb_dep.Orb.init();

Providing ORB Properties with Username, Password, and Role

If you execute the ORB.init method that provides the ORB properties, username, password, and role as parameters, it does the following:

You would use this method when your client chooses to not use JNDI for ORB initialization and it receives a reference to an existing object from another client. To access an active object within a session, the new client must authenticate itself to the database in one of two ways:

This method is how a second client invokes an active object in an established session.

Syntax
public org.omg.CORBA.ORB init(String un, String pw, String role,
	boolean ssl, java.util.Properties props);

Parameter   Description  

String un  

The username for client-side authentication.  

String pw  

The password for client-side authentication.  

String role  

The role to use after logging on.  

Boolean ssl  

If true, SSL is enabled for the connection. If false, a NON-SSL connection is used.  

Properties props  

Properties that are used by the ORB.  

CORBA ORB Interface

If you have implemented a pure CORBA client--that is, you do not use JNDI--you must set the following properties before the ORB initialization call. These properties direct the call to the Aurora implementation, rather than the Java 2 implementation. This ensures the behavior that you expect. The behavior expected from Visibroker is as follows:

Example 3-2 Assigning Visibroker values to OMG properties

The following example shows how to set up the OMG properties for directing the OMG CORBA init method to the Visibroker implementation.

System.getProperties().put("org.omg.CORBA.ORBClass",
"com.visigenic.vbroker.orb.ORB"); System.getProperties().put("org.omg.CORBA.ORBSingletonClass",
"com.visigenic.vbroker.orb.ORB");

Or you can set the properties on the command line, as follows:

java -Dorg.omg.CORBA.ORBClass=com.visigenic.vbroker.orb.ORB
-Dorg.omg.CORBA.ORBSingletonClass=com.visigenic.vbroker.orb.ORB

Backwards Compatibility with 8.1.5

The tools provided with Oracle8i, such as publish, have been modified to work with either a JDK 1.1 or Java 2 environment. However, any code that has been generated or loaded with the 8.1.5 version of any tool, will not succeed. Make sure that you always use the 8.1.6 version of all tools. This rule applies to your CORBA stubs and skeletons. You must regenerate all stubs and skeletons with the 8.1.6 IDL compiler.

CORBA Callbacks

This section describes how a CORBA server object can call back to a client. The basic technique that is shown in this example is the following:

IDL

The IDL for this example is shown below. There are two separate IDL files: client.idl and server.idl:

/* client.idl */
module client {
  interface Client {
    wstring helloBack ();
  };
};

/* server.idl */
#include <client.idl>

module server {
  interface Server {
    wstring hello (in client::Client object);
  };
};

Note that the server interface includes the interface defined in client.idl.

Client Code

The client code for this example must instantiate the client-side callback object and register it with the BOA so that it can be accessed by the server. The code performs the following steps to do this:

The code to perform these steps is as follows:

org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init ();
org.omg.CORBA.BOA boa = orb.BOA_init ();
ClientImpl client = new ClientImpl ();
boa.obj_is_ready (client);

Finally, the client code calls the server object, passes it a reference to the registered client-side callback object, and prints its return value, as follows:

System.out.println (server.hello (client));

Callback Server Implementation

The implementation of the server-side object is very simple:

package serverServer;

import server.*;
import client.*;

public class ServerImpl extends _ServerImplBase {
  public String hello (Client client) {
    return "I Called back and got: " + client.helloBack ();
  }
}

The server simply returns a string that includes the string return value from the callback.

Callback Client-Server Implementation

The client-side callback server is implemented like this:

package clientServer;

import client.*;

public class ClientImpl extends _ClientImplBase {
  public String helloBack () {
    return "Hello Client World!";
  }
}

The client-side object is just like any other server object. But in this callback example it is running in the client ORB, which can be running on a client system, not necessarily running inside an Oracle8i database server.

Printback Example

Among the CORBA examples shipped on the CD there is a very interesting variant of the callback example called printback. This example shows how a server object can call back to a client to print strings from the server on the client's console. You can use code like this for debugging a running server object.

Using the CORBA Tie Mechanism

There is only one special consideration when you use the CORBA Tie, or delegation, mechanism rather than the inheritance mechanism for server object implementations. In the Tie case, you must implement the oracle.aurora.AuroraServices.ActivatableObject interface. This interface has a single method: _initializeAuroraObject().

(Note that earlier releases of the Oracle8i ORB required you to implement this method for all server objects. For this release, its implementation is required only for Tie objects.)

The implementation of _initializeAuroraObject() for a tie class is typically:

import oracle.aurora.AuroraServices.ActivatableObject;
...
public org.omg.CORBA.Object _initializeAuroraObject () {
  return new _tie_Hello (this);
...

where _tie_<interface_name> is the tie class generated by the IDL compiler.

You must also always include a public, parameterless constructor for the implementation object.

See the tieimpl example in the CORBA examples set for a complete example that shows how to use the Tie mechanism. See also "tieimpl" for the code.

Interoperability with non-Oracle ORBs

You can interoperate with Oracle8i from a client that uses another vendor's ORB. To do so, the vendor must provide the functionality that Oracle8i uses by being part of the database: functions such as session-based connections, extended CosNaming functions, and the login protocol. To provide this functionality, your ORB vendor must work with Oracle's Product Management to provide libraries for you.

All client-side functionality has been packaged into aurora_client.jar. This JAR file has been broken into two JAR files for interoperating with your ORB vendor:

Your ORB vendor needs to provide you the aurora_orbdep.jar file. Thus, you include their aurora_orbdep.jar file and the Oracle-provided aurora_orbindep.jar file to replace aurora_client.jar.


Note:

If you do not remove aurora_client.jar file from your CLASSPATH, you will be using Oracle's classes, instead of your ORB vendor's classes.  


The aurora_orbdep.jar includes the following functionality:

Function   Description  

login  

The login protocol performs the challenge/response protocol for authenticating the client to the database. See "IIOP Security" for more information.  

bootstrap  

The boot service obtains key services, such as CosNaming.  

extended CosNaming  

The Aurora ORB extended CosNaming to automatically instantiate an object upon first lookup.  

Session IIOP  

Session IIOP is implemented to allow one client connect to more than a single IIOP session at the same time. See "Configuring CORBA and EJB in JServer" for more information.  

Credentials  

The security context interceptor for the credential type of authentication.  

Java Client using Oracle ORB

You perform the following if you choose to use the Oracle-provided ORB on your client:

  1. Put aurora_client.jar in a directory that exists in the CLASSPATH.

  2. Compile and run your CORBA application.

Java Client using non-Oracle ORB

You perform the following if you choose to use another vendor's ORB on your client:

  1. Put aurora_orbindep.jar in a directory that exists in the CLASSPATH.

  2. Contact your ORB vendor to receive their aurora_orbdep.jar.

  3. Put their aurora_orbdep.jar in a directory that exists in the CLASSPATH.

  4. Compile and run your CORBA application.


    Note:

    If you do not remove aurora_client.jar file from your CLASSPATH, you will be using Oracle's classes, instead of your ORB vendor's classes.  


C++ Client Interoperability

With C++ clients, the ORB vendor must provide the aurora_client.jar file functionality in shared libraries. The vendor will make use of Oracle-provided C++ login protocol for authentication. All clients are required to authenticate themselves to the database. One of the methods for authenticating is through the login protocol.

The login protocol is an Oracle-specific design, used for logging in to a database by providing a username and password to authenticate the client. The following example shows how to write a sample C++ CORBA client to Oracle8i. This example uses the Visigenics C++ ORB for its client-side ORB.

Example 3-3 C++ client using login protocol to authenticate

The following C++ client uses the Visigenics C++ ORB for the client-side ORB. Your implementation can be different, depending on the type of ORB you use.

#include <Login.h>
#include <oracle_orbdep.h>

// set up host, port, and SID
char *sid = NULL;
char *host = argv[1];
int port = atol(argv[2]);
if(argc == 4) sid = argv[3];

// set up username, password, and role
wchar_t *username = new wchar_t[6];
username[0] = 's';  
username[1] = 'c';  
username[2] = 'o';  
username[3] = 't';  
username[4] = 't';  
username[5] = '\0';  

wchar_t *password = new wchar_t[6];
password[0] = 't';  
password[1] = 'i';  
password[2] = 'g';  
password[3] = 'e';  
password[4] = 'r';  
password[5] = '\0'; 

wchar_t *role = new wchar_t[1];
role[0] = '\0';

// Get the Name service Object reference 
AuroraServices::PublishingContext_ptr rootCtx = NULL;

// Contact Visibroker's boot service for initializing
rootCtx = VisiCppBootstrap::getNameService (host, port, sid);
  
// Get the pre-published login object reference
AuroraServices::PublishedObject_ptr loginPubObj = NULL;    
AuroraServices::LoginServer_ptr serv = NULL;
CosNaming::NameComponent *nameComponent = new CosNaming::NameComponent[2];

nameComponent[0].id = (const char *)"etc";
nameComponent[0].kind = (const char *)"";
nameComponent[1].id = (const char *)"login";
nameComponent[1].kind = (const char *)"";

CosNaming::Name *name1 = new CosNaming::Name(2, 2, nameComponent, 0);

// Lookup this object in the Name service
CORBA::Object_ptr loginCorbaObj = rootCtx->resolve (*name1);

// Make sure it is a published object
loginPubObj = AuroraServices::PublishedObject::_narrow (loginCorbaObj);

// create and activate this object (non-standard call)
loginCorbaObj = loginPubObj->activate_no_helper ();
serv = AuroraServices::LoginServer::_narrow (loginCorbaObj);
      
// Create a client login proxy object and authenticate to the DB
oracle_orbdep *_visi = new oracle_orbdep(serv);
Login login(_visi);
boolean res = login.authenticate(username, password, role);

Debugging Techniques

Until Java IDEs and JVMs support remote debugging, you can adopt several techniques for debugging your CORBA client and server code.

  1. Perform stand-alone ORB debugging using one machine and ORB tracing.

    Debug in a single address space, on a client system. Use of an IDE for client or server debugging is optional, though highly desirable.

  2. Use Oracle8i trace files.

    The output of System.out.println() in the Oracle8i ORB goes to the server trace files. The directory for trace files is a parameter specified in the INITSID.ORA file. Assuming a default install of the product into a directory symbolically named ORACLE_HOME, then the trace file would appear as

    ${ORACLE_HOME}/admin/<SID>/bdump/ORCL_s000x_xxx.trc
    
    

    where ORCL is the SID, and x_xxx represents a process ID number. Do not delete trace files after the Oracle instance has been started, or no output is written to a trace file. If you do delete trace files, stop and then restart the server.

  3. Use a single Oracle MTS server.

    For debugging only, set the MTS_SERVERS parameter in your INITSID.ORA file to MTS_SERVERS = 1, and set the MTS_MAX_SERVERS to 1. Having multiple MTS servers active means that a trace file is opened for each server process, and, thus, the messages get spread out over several trace files, as objects get activated in more than one session.

  4. Use "printback" to redirect System.out.

    You can use the technique demonstrated in the example program "printback" to redirect System.out and System.err println to the client system console.

Perhaps the best way to develop and debug Java/CORBA code is to use either the second or third technique described above, then deploy into the Oracle8i ORB.

For More Information

This section lists some resources that you can access to get more information about CORBA and about CORBA application development using Java.

Books

The ORB and some of the CORBA services supplied with Oracle8i JServer are based on VisiBroker for Java code licensed from Inprise. Programming with VisiBroker, by D. Pedrick et al. (John Wiley and Sons, 1998), provides both an introduction to CORBA development from the VisiBroker point of view and an in-depth look at the VisiBroker CORBA environment.

Client/Server Programming with Java and CORBA, by R. Orfali and D. Harkey (John Wiley and Sons, 1998), covers CORBA development in Java. This book also uses the VisiBroker implementation for its examples.

You should be aware that the examples published in both of these books require some modification to run in the Oracle8i ORB. It is better to start off using the examples in the Appendices to this Guide, which are more extensive than the examples in the books cited and demonstrate all the features of Oracle8i CORBA. See also Appendix C, "Comparing the Oracle8i JServer and VisiBroker VBJ ORBs" for a discussion of the major differences between VisiBroker for Java and the Oracle8i implementation.

URLs

You can download specifications for CORBA 2.0 and for CORBA services from links available at the following web site:

http://www.omg.org/library/downinst.html

Documentation on Inprise's VisiBroker for Java product is available at:

http://www.inprise.com/techpubs/visibroker/visibroker33/




Prev

Next
Oracle
Copyright © 1999 Oracle Corporation.

All Rights Reserved.

Library

Product

Contents

Index