Oracle8i’s ORB does the trick
Develop scalable and secure Java CORBA applications with Oracle8i
Oracle8i features a built-in JVM, an embedded ORB (object request broker) based on OMG’s (Object Management Group) CORBA specification, and useful services such as an EJB component model. In this article, we’ll look at the Java-based Oracle8i Aurora ORB’s distinguishing features and demonstrate how to develop applications with it. OMG’s CORBA architecture is the industry’s most-deployed distributed component model. Oracle8i’s integrated JVM uses the database’s session-based architecture to achieve better scalability, so Oracle8i’s embedded ORB provides scalability and rich functionality interoperable with other ORBs.
Oracle8i also comes with an EJB container. EJB is simpler from an end user’s perspective because of its declarative security and transactional attributes, but a large number of applications still use ORB technology because of its language independence. Oracle8i’s ORB and embedded services such as NameService and Security Service make it a powerful component model. The clear ORB interoperability standards also enable the mixing and matching of ORBs from different vendors and languages, as well as support for legacy applications written in non-Java languages. We’ll also look at guidelines for interoperability with other ORBs and support for C++ clients. So, if you are a Java developer wanting to develop large-scale distributed and scalable applications, this article is for you.
Note: From this point on, I’ll refer to the Oracle8i ORB as Aurora ORB, Oracle’s code name for its ORB product. The ORB features listed will be part of Oracle’s 8.1.6 release, which will support both JDK 1.1 and JDK 1.2.
Features of Aurora ORB
Oracle 8.1.6’s Aurora ORB, based on Inprise’s vbroker Java code base, complies with OMG CORBA 2.0 specifications (with one exception: it supports GIOP 1.0 but not GIOP 1.1). Aurora ORB’s current version supports BOA (Basic Object Adapter) but not POA (Portable Object Adapter). Nearly all applications access a database at some point. Since Aurora ORB runs inside the database, it facilitates ORB-server applications’ database access by using the default JDBC connections. Details can be found in Oracle JDBC manuals. (See Resources for a link.)
Aurora ORB’s features include:
- The ORB core
- The COS naming and bootstrapping services
- Implicit activation of factory objects on first lookup
- JNDI SPI (Service Provider Interface) for COS naming
- Strong Security (authentication and authorization) services that do security checks at various levels
- IIOP/SSL (both server-side and client-side authentication)
- Session-based IIOP, whereby a single client can access multiple sessions
- OTS/JTS supporting single resource
- Interface Repository (beta)
- Highly scalable server architecture
- Caffeine (generates IIOP stubs from a Java class without using IDL files)
Developing CORBA applications
Except for small variations in exporting (to a name service) and activating objects, writing an application for CORBA is generally the same as for almost any ORB, including Aurora ORB. However, there is one clear difference in Aurora ORB: because its server code runs in the database, you need to load the Java classes to the database, as explained below. Let’s take a step-by-step look at how to write a CORBA application with Aurora ORB:
- Define interfaces and write the IDL (or use Caffeine): You need to define your server-object interfaces, in the form of a language-neutral CORBA IDL file.
- Code generation: Run the aforementioned IDL file through the
idl2java
compiler, thus generating the necessary client stubs, server skeletons, and utility classes such asHelper
andHolder
. - Server-object implementation: Implement your server object. The implementation class should usually extend the generated skeleton-implementation base class. Java doesn’t support multiple inheritance, so to extend your implementation class, the object implementation needs to implement an interface class (called
<your_interface_name>Operations.java
) known as a tie mechanism, rather than extending the skeleton-implementation base class. After the tie mechanism, the object implementation also needs to implement Aurora ORB’sActivatableObject
interface.ActivatableObject
has one method:initializeAuroraObject()
, which the ORB will invoke upon activation. You’ll also need to do the tie initialization in theinitializeAuroraObject()
method. - Client implementation: Implement your client application.
- Load the Java classes into the database: As mentioned earlier, loading the Java classes into the database is an unnecessary step for stand-alone ORBs running outside a database. Use the
loadjava
tool to load the classes. - Publish the objects to the COS name service: Publish the server object to the COS name service while specifying the requisite security access permissions.
One of the first steps in locating CORBA server objects is to find BootService/NameService, which can look up the requested server objects. With that in mind, let’s turn our attention to bootstrapping.
How to use bootstrapping and the JNDI interface
Aurora ORB implements the BootService mechanism developed by Oracle and others a couple of years ago. BootService defines an interface so it can register services and query for those services. BootService runs on a well-known port for easy client access. Constructing the BootService
client-proxy object reference on the fly is simple because of the service’s well-known port and object key. ORB clients will first resolve BootService, then query for the name-service reference. Then, using the standard COS name
service interface, one can interact with the name service for binding and resolving objects.
Oracle has extended the COS name
service interface to support automatic object activation and to return appropriately narrowed-down objects upon lookup. In addition, Oracle implemented the URL-based JNDI SPI for the COS name
service, so Java clients can simply perform JNDI object lookups. The JNDI lookup returns the appropriately narrowed-down CORBA objects, and the clients can invoke methods on those objects, just like on regular Java objects. In other words, all the complexity of locating the name service, etc., is taken care of by JNDI lookup; the client applications need not worry about the details. However, if a sophisticated client wants to directly use the name service, it may do so using the standard CORBA interface. Oracle ships examples of this sort with the release, and provides a command line tool to register objects with its name service. (See Resources.)
Currently, only top-level stateless factory objects can be published with the name service. User-created transient objects cannot be registered with the service. This restriction will be lifted in the future release; Oracle will support a pure JNDI-based name service that will include federation across different name spaces.
In the next section, we’ll examine a new concept called session-based IIOP, an essential feature for scalability and middle-tier server applications.
Session-based IIOP
CORBA object references are usually distinguished by their host, port, and object key tuples. The ORB runtime typically decides to make new connections to the server only if there is no existing connection to the host and port specified by the object reference it is trying to access. If there is an existing connection, it will send the requests to any objects with that host and port on the same connection. The ORB server dispatcher hands the request to the appropriate object, based on the request’s object key. Consequently, CORBA doesn’t have the concept of sessions, which is important for scalability. With the existing mechanism, there is no way to distinguish object references in different sessions. Obviously, an Oracle database is session-oriented. To take advantage of this, Oracle introduced an additional parameter for distinguishing object references, namely the session in which they are activated. Oracle used the standard component tag method to insert this session information in the object reference.
The connection management in the ORB client now uses not only the host and port, but also the session information — called session-based IIOP. There is no change in the IIOP wire format, allowing a single client to explicitly access or create objects in multiple sessions. As per OMG specifications, the implementation is flexible enough that third-party ORBs that don’t support session-based IIOP will simply ignore additional component tags and do the normal connection management. Moreover, Oracle reserved a separate server port for connecting with regular IIOP connection management.
The above discussion demonstrates a feature whereby a single client will be able to access multiple server sessions, a feature required to deploy Oracle8i as a middle tier. On the other hand, there are also cases when multiple CORBA clients need to be able to go into a single session. For example, if a client created an object reference in a session and passed it along to another client, that second client should be able to go to that session to access the same instance. Oracle achieves this by implementing session routing on the server. Session routing remains transparent to clients and requires no changes to either the client or the client ORB.
Oracle is in the process of including the session component tag in the standard CORBA specification.
In addition to robustness and scalability, security is an important part of any application. Having talked about the scalability aspects through session-based architecture, it’s time now to consider the security aspect of the Aurora ORB.
Security and SSL
Appropriate security checks are made at various levels before access is given to a session or object in the server. The database is a highly secure environment and you’ll need to authenticate to it before you can do anything. Since the CORBA server code runs inside the database, the same rule applies. Therefore, in the case of CORBA clients, Oracle provides several means of authentication:
- Pass in the username/password (supplied by the user) implicitly in a security context to the server on the first message
- Explicitly log in using a prepublished CORBA login object
- Use SSL client-side authentication
Examining these mechanisms in detail is beyond the scope of our discussion, but we can focus on some important considerations. To begin, the username/password is never sent naked to the server. It is either encrypted like an SSL connection or a challenge/response protocol is used. The clients indicate programmatically what type of authentication mechanism they prefer. In the case of username/password (whether it is a non-SSL connection or a server-side-auth-only SSL connection), the database session is authorized to that user. In the case of SSL client-side authentication, the client’s certificate is used (actually, the DN in the certificate is treated as a global user name) to authorize the session.
Once the user is authenticated to the database and a session is created and authorized to that user, other security checks come into play. In case of security violations, a CORBA NO_PERMISSION
system exception is thrown, with appropriate minor codes. For details on how to explicitly grant access to items at various levels, refer to the Oracle8i manuals. (See Resources.) Although not a complete list, here are some of the security checks:
- The user must first authenticate to the database using one of the mechanisms discussed above. Even if the user is connecting to an existing session, authentication is still necessary.
- If the user doesn’t have read and execute access to the object being looked up in the name service, access will be denied.
- If the user doesn’t possess execute privileges on the actual implementation class, access will be denied.
- A user can’t access classes in someone else’s schema unless explicitly granted permission.
- Security checks at various levels are made as per the Java 2 security model (e.g. performing socket and file operations, reading system properties, and debugging privileges).
- General database security checks apply while doing any SQL (using JDBC or SQLJ).
- If the server enforces use of SSL, connections will be refused for non-SSL clients.
Sample code
Now that we’ve looked at Aurora ORB’s features and concepts, let’s learn how to write a simple “Hello, World” non-SSL CORBA application with Oracle8i.
Interface definition
Let’s say the following is the definition of a Hello
object expressed in the IDL file hello.idl
:
module hello {
interface Hello {
wstring helloWorld ();
};
};
As you can see, the Hello
object’s single method — helloWorld()
— returns a string of wide characters (capable of storing multi-byte characters).
Code generation
We now need to run our IDL file through the IDL compiler to generate the client stubs, server skeletons, and some utility classes. At the command line, type:
idl2java hello.idl
The above command will produce stub/skeleton files in the package hello
, as specified in the IDL file (module specification).
Server implementation
Let’s now implement the hello server object as follows (in HelloImpl.java
):
package helloServer;
import hello.*;
public class HelloImpl extends _HelloImplBase {
public String helloWorld() {
String v = System.getProperty("oracle.server.version");
return "Hello client, your javavm version is " + v + ".";
}
}
The server-object implementation employs the helloServer
package, as seen above. The helloWorld()
method simply returns a string that tells what version of Oracle the server is using.
Client implementation
Now let’s write a simple client to look up the Hello
object in the name server and invoke the helloWorld()
method on it (see Client.java
):
import hello.Hello;
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 {
if (args.length != 4) {
System.out.println("usage: Client serviceURL objectName user password");
System.exit(1);
}
String serviceURL = args [0];
String objectName = args [1];
String user = args [2];
String password = args [3];
Hashtable env = new Hashtable();
env.put(Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");
env.put(Context.SECURITY_PRINCIPAL, user);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);
Context ic = new InitialContext(env);
Hello hello = (Hello) ic.lookup(serviceURL + objectName);
System.out.println(hello.helloWorld());
}
}
The above client first sets up the properties for the JNDI lookup. URL_PKG_PREFIXES
simply indicates where the SPI drivers are available for the URL prefix that will be passed to the lookup()
method. In this case, it is called sess_iiop
, as will be evident below. SECURITY_PRINCIPAL is set as the user name to connect to the database, while SECURITY_CREDENTIALS is set as the corresponding password. Next, we set the authentication mechanism as NON_SSL_LOGIN, meaning an explicit login object mechanism (this uses a challenge/response protocol, as you will recall) using a non-SSL connection. The client then simply creates a JNDI initial context and does a lookup passing the object name. The JNDI lookup returns the already narrowed-down object to the given type (in this case Hello
, as explained above), and the client simply invokes the helloWorld()
method. (For details on various other login mechanisms and JNDI environment properties, see Resources for the Oracle8i documentation.)
Compile it
Now that we have written all the necessary code, we should compile all these files, including the code generated by the IDL compiler. To that end, it’s convenient to put all these class files in a jar file — server.jar
. Be aware that Oracle8i 8.1.6 supports both JDK 1.2 and JDK 1.1 on the client side.
Load the classes into the database
As explained earlier, since the server code runs inside the database, the Java classes need to be uploaded into the database. To accomplish this, we turn to the loadjava
tool. At the command line, type:
loadjava -oci8 -u scott/tiger -v -r -f server.jar
In the above command, -oci8
indicates we’ll use the OCI driver (as opposed to thin JDBC) while talking to the server. Scott/tiger
is the username and password (so the classes will now load into Scott’s schema.) Next, -v
indicates verbose mode, and -r
indicates the tool should resolve all the classes being loaded for their transitive closure — useful for early diagnostics in case of errors such as missing classes. -f
indicates that loadjava
should replace the classes if they already exist. server.jar
represents the resulting file with all the compiled classes.
Publish the object to the name service
The next step is to publish the Hello
object to the name service, so that clients doing JNDI lookups can discover it. We do this with a tool called publish
, as seen below:
publish -republish -u scott -p tiger -schema SCOTT -s sess_iiop://localhost:2481:ORCL /test/myHello helloServer.HelloImpl hello.HelloHelper
Table 1 describes the command line options given above.
-republish |
Replace the entry if it already exists |
-u |
Username |
-p |
Password |
-schema |
Find the classes for this object’s implementation in Scott’s schema |
-s |
The URL of the boot service in the form: sess_iiop://host:port:oracle_sid |
/test/myHello |
The object name under which the Hello object is published. |
helloServer.HelloImpl |
The object’s implementation class name |
hello.HelloHelper |
The helper class name used to narrow down the object type. |
Run
Now we’re all set to run the client program, after appropriately setting the classpath. At the command line, type:
java Client sess_iiop://localhost:2481:ORCL /test/myHello scott tiger
You should see the output as:
Hello client, your javavm version is 8.1.6.
Invoke CORBA objects from Java stored procedures
So far, we’ve learned how, using Oracle8i, to develop client/server applications where the standalone client runs outside the database. Needless to say, the server can act as a client when talking to other servers. Moreover, it is possible to write the CORBA client as a Java stored procedure and invoke the procedure through either JDBC or an existing tool such as sqlplus
. This is particularly useful for people accustomed to developing stored procedures, but interested in using new middleware technology — the ORB, EJB, and so on — provided in Oracle8i.
For the Java program to run in the stored procedure, we’ll need to define a PL/SQL wrapper. The Java code could remain exactly the same, as if it were run outside the database. Details on this can be found in Oracle8i manuals. (See Resources.)
Debug
Every development environment requires some sort of debugging facilities. Standalone Java applications can be debugged using the standard jdb
tool. Debugging distributed applications, on the other hand, is more cumbersome. Oracle8i 8.1.6 includes a server-side debugger that follows the standard Java remote debugging protocol, so the user can remotely debug the application’s server portion. With the CORBA debugger object, prepublished with the name service, applications can invoke the methods to start the debug agent. We can then employ the standard jdb
to attach to that remote agent and debug the server application. For details, please refer to the manuals in Resources.
Advanced topics: a look at presentations
As a topic for further research, Oracle8i includes a concept called presentations — a means whereby any new client/server protocol can be implemented with no concern about issues such as network communications, scalability, load balancing, or threading. Presentations allow developers to concentrate on their protocol/logic implementation alone. Oracle implemented the Aurora ORB itself (as well as the component models that 8i supports, including the ORB, EJB, the Webserver/servelets, and so on) as a presentation. Presentations are particularly useful to customers or vendors who develop some sort of bridging software (to support legacy applications) that interoperates with the standard component models.
Interoperability/C++ clients
Last, but certainly not least, interoperability with other vendors is an important consideration. The goal is to allow clients that use ORBs from other vendors to successfully talk to Oracle8i CORBA servers. Although, at the wire level, IIOP is interoperable out of the box, more often than not different ORB vendors use different bootstrapping, security, or activation models. As a result, Oracle came up with a small list of issues that ORB vendors will need to address to allow clients to seamlessly interoperate with the Oracle8i server. A couple of ORB vendors have already successfully done interoperability testing with Oracle8i ORB.
As for C/C++ ORBs, Oracle also provides a C++ version of its Login
object to log on to the database so clients using C/C++ ORBs can also interoperate with Oracle8i CORBA servers.
Conclusion
Aurora ORB is a unique product because of its built-in security, simplicity, and scalability. If you have read this entire article, you have learned to develop secure Java CORBA applications using Oracle8i, without having to solve the issues of networking, load balancing, security, and so on. It is also worthwhile to note that the applications you develop are interoperable with applications developed with other vendors’ ORBs. More advanced users will also note how simple it is to create their own component model using the Presentations scheme.
Future versions of Aurora ORB will support POA, GIOP 1.2, IIOP firewall, standard objects by value (OBVs), and many other enhancements to keep up with the ongoing OMG standards. As part of the 8.1.7 release due out this summer, look for a generic name service with which arbitrary objects can be registered and activated.