ORB Notes

Ken Cavanaugh

5/12/06

Abstract: Comprehensive documentation on the Sun Java ORB.

Contents

Chapter 1  Starting Points

1.1  Changes for the GlassFish-CORBA project

This document originally described CORAB development internally in Sun’s TeamWare environment. We have since switched to using Mercurial, both internally and externally. The glassfish-corba project still uses CVS, but only to maintain easy access to the documents.

Some of the policies and procedures described here will be changed as we continue moving all of our work to the open internet glassfish-corba project.

1.2  ORB Development procedures

Over the years, we have established a fairly successful development methodology for working on the ORB. Any piece of work from a minor bugfix to a major new subsystem iterates over the same stages one or more times. I’ll discuss this briefly here, but the details will appear below.

  1. Discuss or document the design. This can take many forms, ranging from an informal discussion to a written document. Written documents have in the past been done in many different formats, including email, text files, FrameMaker documents, StarOffice documents, HTML, XML and DocBook, and LATEX (about the only thing none of us have used seems to be Word!). Going forward, all of these are still viable in different ways, but I’d really like to make sure that we capture all of the design efforts into our documentation collection. All current documentation is found in the CORBA workspace in the www directory, and all future documentation should either be there, or in the actual source code.
  2. Develop the code. This include writing both the code to implement the change and the code to test it. Bugs need a test that fails before the change is made, and that passes after the change. All tests must be incorporated somewhere in the CORBA test suites (see sub:Test-Suites) so that they can be run automatically. Test execution speed is important, so make the test as fast as possible. Please follow the principles discussed in sec:Thoughts-on-Middleware and in sec:ORB-Coding-Practices.
  3. Verify that nothing has been broken by the change. This requires running all of the CORBA test suites, and fixing any problems that show up (see sub:debugging-tests for help on debugging tests).
  4. Prepare a webrev for a code review. The webrev tool is available on SWAN (Sun’s internal network) in /java/devtools/share/bin. Read the comments in this kshell script for details. (TBD: where is webrev outside of Sun?). Webrevs have the advantage that they can be archived standalone, and this is probably a good thing to do. Another viable option is to use a tool like meld (see meld http://meld.sourceforge.net/) that can do directory diffs.
  5. Have at least 1 (and preferably more) developer from the GlassFish-CORBA team review the changes. Make any necessary corrections from the review, and re-review if necessary.

    TBD: Currently we conduct all code reviews in real time, usually via phone conference (since this has been a distributed team for a long time). We need to investigate this in the open source world, and find a more scalable method that works asynchronously. Something like Google’s Mondrian would be a good choice, if it were available.

1.3  Workspace Structure and Builds

The workspace is divided into a number of directories. The current structure will change somewhat after I finish work on another putback that restructures the workspace to simplify its organization and remove obsolete junk.

Directory
Purpose
build
created during build process
build/classes
compiled classes
build/gensrc
Java source code generated by build
build/lib
miscellaneous idl files for the interface repository
build/release/libJar files that are the build results:
  • idlj.jar: the IDL compiler
  • omgapi.jar: the OMG API classes that are NOT part of JDK 5 or 6
  • orblib.jar: the ORB library files from the orblib directory
  • peorb.jar: the main ORB files
build/rename/ee
Where the ant rename target puts a copy of the workspace
lib
various jar files needed to build and test the ORB:
  • emma (for code coverage)
  • ejb 2.1 APIs (for a codegen test)
  • japex (to compile against for StandardTest)
  • jscheme (used for generating log wrappers)
  • junit and testng (for testing)
make
contains ant build files
nbproject
contains NetBeans support to treat workspace as a NetBeans project
orblib
Standalone ORB library source files (delivered to orblib.jar)
src
main ORB source code (delivered to peorb.jar)
test
ORB test source (and orblibrary as well at present)
tools
IOR parser

1.3.1  docs

There are quite a few files here. This will eventually include all of the useful files from /java/j2ee/CORBA. Most of the doc files in the current workspace are useless, with one important exception: the eea1 directory. These are Everett Anderson’s notes on the design and implementation of the GIOP layer, and are still quite useful. This also documents the RMI-IIOP stream format version 2 implementation.

1.3.2  ORB source

Generally, most ORB implementation source packages have two parts: an interface defined in an spi package, and an implementation in an impl package. We will generally discuss these together.

The ORB source code is divided into several parts:

src/solaris
Useless and should be removed.
src/share/classes
This contains the main part of the source code. It can be further divided into:
org
OMG standard CORBA APIs
javax
OMG standard RMI-IIOP APIs
sun/rmi
rmic compiler
sun/corba
CORBA Bridge class, which isolates ORB dependencies on non-public JDK APIs
com/sun/corba/ee
Main body of ORB source code (automatically renamed to ee package for app server build)
GiopIDL
IDL files for GIOP protocol definitions
PortableActivationIDL
Old ideas for ORBD rewrite; to be removed
experimental/portableactivation
Part of ORBD rewrite; to be removed
impl/spi
The main part of the ORB implementation
activation
ORBD
copyobject
Object copier code with special hooks for CORBA
corba(impl)
Implementation of OMG CORBA APIs (but not org.omg.CORBA.ORB)
dynamicany
Dynamic Any support
encoding
Input and output streams for CDR and JSG (Java Serialization for GIOP). PEPt encoding level code.
extension(spi)
Some AS-specific CORBA policies used by EJB for creating POAs.
folb
Code for support IIOP failover and load balancing in GlassFish v2
interceptors(impl)
Portable Interceptor implementation
io(impl)
Code to analyze classes and implement streams for GIOP. This is the valuehandler implementation, which can be used by other ORBs.
ior
How we represent IORs
javax(impl)
RMI-IIOP implementation
legacy
Some AS-specific extensions to interceptors and some connection management support
logging
Logging infrastructure used by generated log wrappers
monitoring
ORB monitoring framework
naming
CosNaming service implementations
oa
Object Adapters. This includes:
poa(impl)
The Portable Object Adapter implementation
toa(impl)
Simple OA used for orb.connect/disconnect (JDK 1.2) and old RMI-IIOP support
rfm
The ReferenceFactoryManager, used to enable suspend/resume of ORB processing for dynamic reconfiguration. Used to support dynamic FOLB in AS9.
*.java
The OA SPI.
orb
The implementation of the ORB class, and associated configuration framework. The configuration code should really be moved into a utility library.
orbutil
(impl) Utilities not related to RMI-IIOP. This includes JDK 1.3.1 backwards compatibility support, the threadpool, and a few other miscellaneous utilities, and ORBConstants. ORBConstants is very commonly used in the ORB and App server and should be moved to an SPI package.
presentation
PEPt presentation level code for dynamic RMI-IIOP
plugin(impl only)
ORB Hardware loadbalancing support
protocol
PEPt protocol level code
resolver
Internal classes used to support string to object reference conversion
servicecontext
Internal representation of GIOP ServiceContexts.
transport
PEPt transport level code
txpoa
TSIdentificationImpl, which is used to connect the ORB and the transaction service.
util
low-level code mostly related to RMI-IIOP.
internal
Old (JDK 1.4 and earlier) ORB and JNI library related classes that we maintain for backward compatibility.
org/omg
Various mostly CSIv2 related protocol definitions (we compile the CSIv2 IDL in the ORB so that the app server CSIv2 implementation can use it).
pept
The PEPt 1.0 code used in the ORB.
com/sun/org/omg
Some not quite standard OMG classes that were still in flux in the CORBA 2.4.1 timeframe (these are internal only, so this doesn’t matter much now)
com/sun/tools/corba/se
A number of tools:
idl
The idl compiler
jmk
The tool used to validate .jmk files against the contents of the source directories
logutil
The source code for the jschemeutil.jar library
timer
XML data for generating timing points
orblib/src/share/classes/com/sun/corba/ee
Classes in the ORB library. There are no dependencies from any of this code to classes in the src hierarchy.
org
Contains the ASM code used in the ORB for dynamic class generation.
impl/orbutil and spi/orbutil
Contains a number of modules:
argparser(spi only)
A general purpose annotation-driven CLI argument parser.
closure
Simple closure support used in a few placescodegen The runtime byte code generator library.
codegen
A general-purpose runtime code generation library.
concurrent
Some concurrent queue implementations needed in the new connection cache.
copyobject
A fast general-purpose object copier.
file(spi only)
A collection of text file utilities used for copyright header processing and workspace renaming.
fsm(spi only)
Finite State Machine library used in the POA to support ActiveObjectMap entry semantics. Should be more widely used in ORB (e.g. connection management). Supports much of the UML state model (but not nested states, and not petri-net style operations)
generic(spi_only)
Some useful utilities related to Java 5 generic (generic Pair, various kinds of generic function classes)
graph(impl only)
Some simple graph utilities for computing transitive closure. Should probably be heavily revised.
jmx
A new framework for using annotation to generate open MBeans
misc(spi only)
odds and ends that don’t fit elsewhere
newtimer
A general-purpose timer framework used to capture BEGIN/END pairs of events.
proxy(spi only)
Utilities related to simplify construction of InvocationHandlers for java.lang.reflect.Proxy.
timer(impl only)
An old, deprecated timer framework.
transport
The new connection cache implementation (not yet integrated in the ORB). A nearly identical version of this code is in Grizzly, and the ORB will eventually use the Grizzly copy.
threadpool
The ORB threadpool implementation that is shared with the app server (should revisit this in light of JDK 5 executors).

1.3.3  ORB Renaming

Most of the ORB is packaged under the com.sun.corba.ee package. A version of the ORB code exists in this package (or in a slightly different version) in every JDK since 1.2. We also deliver the ORB code to the Sun application server (now project GlassFish). To avoid possibly collisions between the classes in the JDK and the classes in GlassFish, we rename all the files in the ORB to the com.sun.corba.ee package.

This rename is done automatically using a Java program (com.sun.corba.ee.spi.orbutil.file.WorkspaceRename) that is part of the ORB library. There is an ant target (rename) for this as well. All that is needed for the rename is:

  1. cd <workspace>/make
  2. ant orb-library (on a new workspace, to build the orblib.jar the first time)
  3. ant rename

(TBD: make rename do a fast check for the orblib.jar, and build it if it is not present. Right now, calling ant orb-library takes around 5 seconds, which is too long, since the incremental rename is <1 second). The ant rename target on a fast local file system should take around 30 seconds or so (much faster than the old scripted version).

Of course, the rename also interferes with the standard edit-compile-test-debug cycle. I have a build environment built around vim that gets around this problem, but it is difficult to impossible to deal with renaming with any IDE that I know of.

The rename is no longer necessary for development in GlassFish-CORBA. Here all that is required is an ant build, from NetBeans, standalone, or from any other tool a developer might care to use. The only time a rename is required is when the jar files are created that are delivered to GlassFish for integration in the app server.

1.3.4  ORB Build Files

The ORB can only be built with ant. All makefile support has been removed.

1.3.4.1  Building the ORB

The ORB can be build either standalone (the mode used to deliver the ORB into GlassFish), or as part of the JDK. Here we just focus on the app server specific build. You MUST use JDK 5 or later.

Assume <ws> is the ORB workspace. The basic build sequence is:

  1. cd <ws>/make
  2. ant rename
  3. cd <ws>/build/rename/ee/make
  4. ant build
  5. ant test (or ant emma to run the tests and generate a coverage report)

Assuming a local file system is used on a fast machine, the rename should take around 30 seconds, and the build 60 seconds or less. The test target builds the tests (build-tests) which takes around 60-90 seconds, and then runs all of the tests, which should take around 40 minutes.

I generally use a script to automate this. It is also possible to do a build from NetBeans. It used to be necessary to rename in order to build and run the tests, but that has been fixed in the GlassFish-CORBA project.

Another small note: it is possible to generate JavaDocs for the CORBA SPI. To do this, simply go to the make directory in the renamed version of the workspace, and run “ant javadoc”. The resulting JavaDocs may then be accessed from <ws>/build/rename/ee/build/release/docs/index.html (exercise for the reader: fix all of the JavaDoc warnings that show up). It should also be possible to create the SPI javadocs without renaming, and the NetBean project supports this as well.

Here is a more detailed description of the ant targets that are useful to a developer:

Target Name
Function
build
builds the ORB library and main ORB code (but not the tests)
build-tests
Runs idlj and rmic on test files, and calls compile-tests
compile-tests
Only compiles the tests (useful if you are changing a test, as build-tests takes a lot longer to run
orb-library
Just build the ORB library (needed for rename)
update-copyright-headers
Update all copyright headers in the workspace to the contents of make/copyright-information/copyright.txt
validate-copyright-headers
Check that the copyright headers match copyright.txt
clean
Removes all generated files, renamed workspace, and test results
clean-emma
Removes emma results
clean-tests
Removes test results
javadoc
generate javadocs for ORB SPI (TBD: need to handle ORB library as well)
findbugs
generate a findbugs report on the ORB
emma
Runs clean-emma, emma-instr, test, emma-report
emma-instr
Instrument all of the class files so that emma can generate a coverage report
emma-report
Generate an emma report (found in <ws>/build/coverage/coverage.html)

There are also a number of targets useful for running tests, which are discussed in 1.4.

1.3.4.2  Structure of the build files

Let’s look at the ant files first. There are several .xml files, all included in build.xml:

build.xml
the main file
jscheme.xml
Ant targets for running jscheme and generating log wrappers
src-idl.xml
Ant targets for generating java from idl for the main ORB code
test-idl.xml
Ant targets for generating java from idl for the ORB tests
test-rmic.sml
Ant targets for generating stub and skeletons using rmic for the ORB tests
test.xml
Ant targets for running the ORB tests.
emma.xml
Ant targets for emma support
findbugs-filter.xml
Used by findbugs to avoid generating bug reports on either the compilers or third-party code.

There are a number of other files as well:

build.properties
Included in build.xml, and used to define the build version and other information for the maven repository importer (TBD: this needs testing and integration for GlassFish v3)
deleted-files.txt
A list of generated files that are deleted in the build, as they are not wanted in the delivery.
glassfish-corba.pom
Part of the maven importer support
runtest
A useful script for running tests as described in 1.4.

1.3.5  tests

The ORB has many tests in several different test suites (docs/TestCases.sxc gives some details of the contents and the numbers of test cases in the test suites, from a rough manual count). The test suites are:

ibm
Old RMI-IIOP tests created by IBM. These are the hardest tests to deal with.
corba
The bulk of our developer tests. Covers all areas of the ORB to some degree.
pi
The Portable Interceptor tests.
naming
Test for the name services.
mantis
Tests specifically for bug fixes made to JDK 1.4.1.
hopper
Tests specifically for bug fixes made to JDK 1.4.2.
copyobject
Tests for the various object copiers. Can also be used as a timing test for streams, which will likely be very important to us.
simpleperf
A simple performance test mainly for colocated calls.

1.3.6  libraries

There are a number of libraries in the lib directory. These are all used either for building or testing.

ejb-2_1-api.jar
Needed for codegen test
emma.jar
Main emma code
emma_ant.jar
Emma’s ant task
ir.idl
Old and obsolete interface repository file (should be deleted)
japex.jar
A version of the Japex performance testing framework (used for compiling StandardTest)
jscheme.jar
Scheme interpreter used for generating log wrapper source files
jschemelogutil.jar
Some simple utilities used with JScheme (source is in the workspace)
junit.jar
JUnit, used for some of the ORB tests
maven-repository-importer-1.1.jar
Part of the maven support for GlassFish (which is incomplete)
orb.idl
A standard IDL file for some standard definitions (not really used)
rt.idl
IDL for the CodeBase interface (part of the SendingContext module; used to enable access to another VM’s typing information for RMI-IIOP)
testng.jar
TestNG, used in some of the ORB tests

1.4  ORB Developer Tests

1.4.1  Test Suites

The ORB developer tests are found in the directories test and optional/test. The makefile for running the tests is in test/make/Makefile, but the test suites can also be run from make/Makefile.corba. This table gives a brief overview of the available test suites:

Suite NameTest Fileant target in

GlassFish-CORBA

Purpose
all-test-allruns all test suites
IBMtest/AllTests.desctest-rmi-iiopOld IBM tests
corbacorba/CORBATests.tdesctest-corbaMost of the newer tests
pipi/PITests.tdesctest-piPortable Interceptors tests
copyobjectcorba/CopyObjectTests.tdesctest-copyobjectcopyobject test
namingnaming/NamingTests.tdesctest-namingnaming tests
hopperhopper/HopperTests.tdesctest-hopperBugfixes for JDK 1.4.1
mantismantis/MantisTests.tdesctest-mantisBugfixes for JDK 1.4.2
simpleperfperformance/Tests.tdesctest-perfco-located call performance test

All of the tests are run from <ws>/make using the appropriate targets. The output of the tests (stdout and stderr) are redirected to log files. These log files are located in <ws>/test/make/gen, under the package name of the individual tests in the test suite. Most of the tests of interest are in the corba test suite, so making those pass first is usually all that is needed (the others tend to pass too, once the CORBA tests pass). This is not always the case, particularly if you are working on the RMI-IIOP code (which is tested in the IBM test suite). These tests are also descibed briefly in <ws>/docs/TestCases.sxc, which is a spreadsheet that roughly counts the number of test cases in each test in each test suite.

Each of the tests in the test suite starts one or more Controllers. A Controller is simply a class that controls a component of a test. For example, many of the tests have 3 controllers: a Client, a Server, and ORBD (which is used mainly for name service, as few tests actually exercise server activation).

Adding a new test is simple: just create a new package for testing, write the test, and add it to the appropriate test file. There is a document that is somewhat helpful in the workspace at

test/src/share/classes/corba/framework/package.html

Especially read the section on the Controller classes, as that is really the heart of the test framework. However, the document is old, and somewhat out of date. You should consider the following additions and changes since the document was written:

static {
System.setProperty( ORBConstants.USE_DYNAMIC_STUB_PROPERTY, ‘‘true’’ ) ;

}

1.4.2  debugging tests

To debug a test, you need to know the name of the controller(s) to which you need to attach a debugger. Controllers are normally created by the methods createORBD, createClient, and createServer. The default names of the controllers are ORBD, Client, and Server, respectively. The createClient and createServer methods can also take a second argument (the first is the class name of the test program) that gives a specific name for the controller.

Given the name of the controller(s) to debug, simply add the argument

-rdebug XXX,YYY

for controllers XXX and YYY (for example) to the end of the test file argument that starts the test.

ORB debug flags can also be passed into a test. To do this, add the argument

-orbtrace XXX:f1,f2;YYY:f3

where the argument is a semi-colon separated. Each element of this list starts with a controller name, followed by a comma separated list of ORB debug flag names (see com.sun.corba.ee.spi.orb.ORB for the current list).

It is also possible to change the log levels so that ORB log information can be displayed on the console (or anywhere else, depending on the log system configuration). This follows the usual log system mechanisms. The ORB logger names are discussed in sec:ORB-Logging.

1.4.3  Debugging and Running a single test

I recently added the capability to run and debug a single test. This requires the runtest script in the make directory. This is a script that calls the run-test-target target in the test.xml file. You can give runtest any arguments that occur in the .tdesc files. To use runtest, cd to <ws>/make and check that runtest is executable. Here are a few examples (assuming the current directory is <ws>/make):

  1. To run the codegen test:
    ./runtest -test corba.codegen.CodegenTest
  2. To attach a debugger to the client controller in the codegen test:
    ./runtest -test corba.codegen.CodegenTest -rdebug client
  3. To see the transport debug output on the evol_client controller in the corba EvolveTest:
    ./runtest -test corba.evolve.EvolveTest -orbtrace evol_client:transport
    (the debug output will be in ../test/make/gen/corba/evolve/evol_client.out.txt)

All tests are run in the environment of the current ORB workspace. Either the renamed or the non-renamed version can be used. This is important because JDK 5 or later contains many of the same classes in the com.sun.corba packages as the workspace. The ant files set up the java -Xbootclasspath argument correctly so that the versions of the com.sun.corba classes in the workspace are used, instead of those in the JDK.

1.5  ORB SQE Tests

Sony Manuel maintains a large collection of CORBA SQE tests in /java/idl/ws/rip/RIP_TEST_MASTER. Read the file DTF_RTM_README.html in this workspace for details (and contact Sony as well). These test are not currently available in GlassFish-CORBA.

We should look at creating a tighter integration between the CORBA dev tests and the SQE tests at some point. In particular, I’d like to have the POA and INS tests run automatically as part of the CORBA dev test cycle.

1.6  Thoughts on Middleware goals

Middleware is a rather complex kind of software to build well. It spans many parts of the computer science discipline, including compilers, operating systems, and network communications. Middleware tends to be complex, long-lived software, and there are many different ways to build it. Our goal in developing the ORB has been to develop very flexible and high-performance middleware while maintaining a clear (if complex) architecture that can be easily composed, ultimately out of re-usable modules. One way to look at an ORB is that ORB.init creates a particular middleware implementation that is specialized to the needs of an application. For example, the behavior of the default ORB in the JDK is rather different from that of the ORB in the app server, even though both share >95% of the same code.

At least the following dimensions must be considered in order to build effective middleware:

Flexibility.
Middleware has been changing constantly since Nelson invented RPC around 1980 or so. Much of Harold’s work on PEPt has been devoted to dealing effectively with this aspect of middleware construction. The following elements often vary indendently of each other:
Presentation.
By this we mean the kinds of data types that may be passed between a client and server (roles here, as any given software entity often acts in both roles), and the APIs and other structures used to collect these kinds of data together.

Examples of data types include the IDL data model from CORBA, the Java data model from RMI, XML schema, ASN.1, SUN RPC, MIME types, and many others.

There are broadly speaking two ways to view the API question: either the system uses some sort of Proxy to make a remote call “look like” a call to an abstraction (method call, procedure call, SmallTalk message send, etc) or the API provides an explicit representation of a request or message that a program can set up with data and then send (CORBA dynamic invocation, Message Oriented Middleware, and others).

Encoding.
Given a structure containing the data from the presentation layer, it needs to be converted from an in memory representation to some representation suitable for transmitting across some serial medium like a TCP connection. This is usually referred to as marshalling and unmarshalling the data, and is often the most expensive operation that middleware performs. In fact, I believe this is the most significant challenge for middleware of all types as network hardware increases in speed.
Protocol.
This is related to but distinct from encoding. Here we are concerned about the kinds of messages that clients and servers exchange. This is mainly about message framing, headers, various kinds of meta-data associated with requests, and the distributed state machines involved in protocol design.
Transport.
Ultimately, some mechanism must be used to transmit data from the client to the server. This can include network protocols, shared memory mechanisms, Solaris doors, and direct calls within the same address space that bypass all such mechanisms (in which case the protocol and encoding are much simplified as well).
Performance.
Everyone always wants middleware to perform as quickly as possible. This is quite a challenge. First, there are many aspects to performance, including:
Latency.
How long does it take to send messages of various types? What about short vs. very long messages?
Throughput.
How many request per second can be sent through the system? This often conflicts with latency. For example, a front end concentrator can help to pump more data through, at the cost of increased latency due to an extra hop.
Creating Endpoints.
Object-Oriented remoting systems like CORBA create, marshal, and unmarshal endpoints (IORs in CORBA) constantly, and the performance of such operations is quite significant.

Today, our latency is poor (except for co-located RMI-IIOP calls, which are highly optimized), our throughput is OK (and this is NIO select related), and the IOR handling is pretty good, although much more is possible.

We need to follow a number of strategies for improving performance:

Reliability.
Middleware is usually used in places that need to run continuously (like the app server). This again implies a number of considerations:
Avoid Memory Leaks.
This has been an issue for the ORB mainly in caching class related data. We have had to make careful use of soft and weak references in a number of places to handle this. Extreme care is needed to avoid leaking direct ByteBuffers, which generally must be pooled to achieve acceptable performance.
Build Clean Code.
This is why we have a strong emphasis on programming to interfaces. Another important aspect of this is only write a piece of code once, and then reuse it.
Use Test Driven Development.
By this I mean that anyone producing a module of code must also produce a set of unit tests (and other tests as needed) to validate the correctness of the module. This also means that as much as possible, bug fixes require a test that fails, and a fix that makes the test pass, and the test must be incorported into the automated build.

1.7  ORB Coding Practices

1.8  Supporting JDK and App Server

The main body of the ORB code (that is, most of the contents of the src directory) must be delivered into both the JDK and the app server without change (other than the automatic rename). This has some interesting implications:

  1. Use extension, not variation. Never create an app server and a JDK version of the same class. Instead, refactor the class so that there is a common base in the core that can be extended, either through inheritance or composition.
  2. Prefer composition to inheritance.
  3. Remember that the io, util, and javax code is shared between our ORB and other (non-Sun) ORBs. This means in particular that other ORBs may be built using our ValueHandler and RMI-IIOP classes. The main issue here is that we cannot assume that the ORB class is always com.sun.corba.ee.spi.orb.ORB. It is OK to create a specialized path for our ORB, but this must always be done with an instanceof check, and handle the non-Sun ORB case correctly.
  4. All behavior exhibited by the JDK ORB must follow the OMG specifications. Mostly this is CORBA 2.3.1, but we follow the semantics of later versions when errors have been corrected. For example, the POA should basically follow the behavior documented for CORBA 3.0 at this point. Note that it is fine to create non-standard extensions to CORBA semantics for the app server: this is exactly what we have done for failover and load balancing support (among others).

Note that there is currently NO plan to integrate the GlassFish v3 ORB into JDK 7: there simply are not enough people working on CORBA to support this. This will lead to an increasing divergence over time between the GFv3 and JDK ORBs, particularly in the following areas:

Chapter 2  The ORB Class

The ORB class is the central control point in the Sun ORB implementation. Here we will examine its structure, the services it provides, and how it is initialized and terminated.

2.1  Inheritance Structure

The ORB class has the longest inheritance chain in the ORB. Constructing a UML diagram for this is difficult (because of the way this works in Java Studio Enterprise) and not very illuminating. Instead, I’ll just list the classes and their main function:

org.omg.CORBA.ORB:
This is the main ORB API defined by the OMG.
org.omg.CORBA_2_3.ORB:
This adds some methods related to value types.
com.sun.corba.ee.org.omg.CORBA.ORB:
This adds register_initial_reference. While the method is an OMG standard, the Java mapping that we used for the ORB API did not include this method, so we put it here.
com.sun.corba.ee.spi.orb.ORB:
This adds all of the internal SPI methods to the ORB.
com.sun.corba.ee.impl.orb.ORBImpl:
This is the ORB implementation.
com.sun.corba.ee.internal.iiop.ORB:
For backwards compatibility with JDK 1.4.
com.sun.corba.ee.internal.POAORB:
For backwards compatibility with JDK 1.4.
com.sun.corba.ee.internal.PIORB:
For backwards compatibility with JDK 1.4.

The first 3 classes together define the standard OMG ORB API. It is split because it evolved in stages, and we cannot add new methods to an interface that a third party may implement. Instead, we extend the API class, allowing older ORB implementations from third parties to continue to work. The classes in the internal package can be ignored. They are present so that any old code written with the ORBClass property set to the class name will continue to work.

2.2  ORB SPI structure

The key part of the ORB SPI is in the com.sun.corba.ee.spi.orb.ORB abstract class. This is an abstract class because it must extend the other ORB abstract API classes defined by the OMG. The ORB class provides the following operations:

Clearly there are a lot of methods in the ORB SPI. A better approach would be to push most of this into the initial references mechanism, using IDL local objects. This would substantially reduce the size of the ORB SPI. But I think we are unlikely to do this, as the impact on the existing code is probably too large.

2.3  ORB Initialization

The ORB initialization code is fairly complicated, but reasonably well structured at this point. I’ll start with a discussion of the ORB configuration framework, then describe the ORB initialization process in moderate detail. I’ll also discuss how this is extended in the app server.

2.3.1  The Configuration Framework

The orb packages contains a mostly general purpose framework for handling configuration. This is divided into several parts:

The following sections will look at these parts in more detail.

2.3.1.1  DataCollector

A DataCollector has a rather simple interface: setParser passes a PropertyParser instance to the DataCollector, which causes the DataCollector to gather together all configuration data from the available data sources into a single instance of Properties. This instance is available in the getProperties method.

The available data sources are:

It is perhaps not immediately obvious why a PropertyParser needs to be passed to the DataCollector. The reason for this is that it is not always possible to simply grab every bit of information from the data sources. But it is possible to get all configuration information for the known property names. So the DataCollector uses the PropertyParser to fetch information for all property names of interest.

Here is a class diagram of the DataCollector classes:

Note that there are 3 different kinds of DataCollector. The Applet and Normal DataCollectors are used with the corresponding ORB.init methods. The PropertyOnly DataCollector is only used internally, when we need to create an internal full ORB instance to support certain operations on the ORB singleton. The DataCollectorFactory class provides static methods for creating the different kinds of DataCollectors.

There are a few issues here that could be revisited:

Fixing these issues would allow reuse of the DataCollector mechanism outside of the ORB, and provide a somewhat cleaner implementation.

2.3.1.2  Operation

The operation interface is simply:

public interface Operation {
Object operate( Object value ) ;

}

The SPI classes OperationFactory and OperationFactoryExt provide a large number of factory methods for creating instances of the Operation interface:

Other Operation implementations can be readily created, but this set is sufficient to handle all ORB configuration parsing (except for URL parsing, which is currently handled by some classes in the resolver package). Extending this framework to handle URL parsing is relatively straightforward, but requires the ability to handle optional data and alternate forms that is not currently present (essentially something like ifAction( predicate, opTrue, opFalse ) would probably take us in the right direction).

The other issue with this is that composing all of the actions in Java is somewhat cumbersome (take a look at ParserTable.makeADOperation for an example). A customized language (e.g. some Lisp macros) could make this much simpler. Combining this with annotation and code generation could reduce the ORB configuration implementation to something like:

@Configuration

public interface ORBData {

@Parse( ‘‘<some expression>’’ )

public String getORBInitialHost() ;

...

}

Pursuing this degree of automation is probably more than is justified by the needs of the ORB.

2.3.1.3  PropertyParser

The DataCollector gives us a way to gather multiple sources of configuration together into a uniform Properties object, and the Operation framework gives us a way to parse Strings into data in many different ways. The PropertyParser ties these two mechanisms together so that we can parse all of the configuration data in a single operation.

A PropertyParser is basically a collection of ParserActions. There are two kinds of ParserActions:

Normal:
Here the property name is found in the Properties object, and the String associated with the name is transformed into a value.
Prefix:
Here the property name is a prefix, and all property names that start with the prefix are transformed into the value.

There is a factory class (ParserActionFactory) that is used to create the two ParserActions.

A PropertyParser is initialized by call its add and addPrefix methods to add the ParserActions that are needed in the PropertyParser. These methods return the PropertyParser so that they can be chained if necessary. Each of these methods takes the following arguments:

propName
which is the property name to which this action is applied
action
which is the Operation performed by this action
fieldName
which is the name in the resulting Map in which the result of the Operation is stored. This is used later (see sub:Base-Classes-for) for storing the results in a configuration object like ORBData.

There are two other important methods in the PropertyParser. The parse method takes a Properties instance and returns a Map from fieldNames to the parsed values. This is the main parsing method in the framework. The iterator method returns an Iterator over the ParserActions, which can be used to find all of the property names in the PropertyParser. This is used by the DataCollector to determine which properties are required.

The following diagram shows the different classes used in the PropertyParser implementation:

2.3.1.4  Base Classes for Parsing Properties

The top of the configuration framework includes classes that can take the Map returned from a PropertyParser and use it to update the fields in a class that is essentially a read-only JavaBean. This is done in ParserImplBase. A further extension to this class in ParserImplTableBase allows the use of a table of ParserData to initialize the Parser.

The ORB configuration data is represented by the ORBData class. It is implemented by ORBDataParserImpl, which extends ParserImplTableBase and uses ParserTable (essentially a large ParserData[]) to provide the initialization data.

The ParserData contains the following information:

Instances of ParserData are created by the factory methods in ParserDataFactory. ParserData is implemented by the NormalParserData and PrefixParserData classes.

2.3.2  Details of ORB.init

Initializing an ORB from an ORB.init() call (the two versions that have arguments) requires several steps:

  1. Select the ORB class that needs to be instantiated.
  2. Create an instance of the class.
  3. Invoke the set_parameters method on the instance.

These steps are all standard. The more interesting part is what happens in set_parameters.

set_parameters proceeds as follows:

  1. Call the preInit method, which sets up most of the configuration independent parts of the ORB (which is not very much). This includes:
    1. Initializing a PIHandler that does nothing, so that the ORB can perform requests before PI has been initialized (which happens near the end).
    2. Create a ThreadGroup for use by the ORB. This is complicated because of some Applet considerations: for details, see the code.
    3. Set up the transient server ID. This is currently just set to System.currentTimeInMillis.
    4. Set up the ORBVersion ThreadLocal.
    5. Initialize some locks.
    6. Initialize the various registries.
    7. Set up invocation info ThreadLocal stacks.
  2. Create a DataCollector that represents the available configuration data for use in creating this ORB instance. ORB.init( String args, Properties props ) uses the NormalDataCollector, while ORB.init( Applet app, Properties props ) uses the AppletDataCollector.
  3. Call the postInit method, which handles all configuration-dependent ORB initialization. This includes:
    1. Setting up the ORBData. This is simple: just construct configData using ORBDataParserImpl and the DataCollector.
    2. Set up the debug flags.
    3. Initialize the monitoring manager, the transport manager, and the legacy server socket manager.
    4. Set up another parser (using the Parser framework) to obtain the ORBConfigurator. Run the ORBConfigurator.
    5. Set up the real PIHandler, replacing the no-op version from the beginning of the initialization sequence.
    6. Set up the thread pool manager and the byte buffer pool

Most of the detailed ORB initialization happens in the ORB configurator, which we will examine next.

2.3.2.1  The ORB configurator

We have two mechanisms for customizing the ORB initialization: the standard (from PI) ORBInitializer, and the ORBConfigurator. Why two? There really is only need for one, except for one really irritating problem: the ORBInitializer does not provide direct access to the ORB or the ORB configuration data (our DataCollector). We also want to be able to have ORB extension parse configuration properties that are not even known in the base ORB configuration (although we don’t currently make use of this). So I chose to create the ORBConfigurator interface.

Looking back on this now, there is an alternative that may have been better: simply extend ORBInitInfo with an internal SPI so that we could access the ORB directly. The current situation is the result of a spec compromise: no one could agree on what operations should be allowed on an ORB instance while it’s in the process of initialization, so a facade object (ORBInitInfo) was specified that sharply restricts what can be done with the underlying ORB instance. Of course, this makes it hard to access anyone’s ORB extensions from inside an ORBInitializer.

The current ORBConfigurator we use is replaceable, as is obvious from the use of the parser to obtain it. For example, we could replace the current Java-code driven approach with an XML-based approach, a Lisp-Sexpression approach, something based on the JINI config language (which is an interpreted simple subset of Java), or some other mechanism. But this does not seem to be needed today.

Here is what the ORBConfiguratorImpl configure method does:

  1. Initialize the default object copiers. This is overridden in the app server init. Object copiers are discussed in more detail in sec:Fast-Object-Copying.
  2. Initialize IOR machinery (see sec:IORs for more details). This involves:
    1. Setting up the tagged profile and tagged profile template factories.
    2. Registering the tagged component factories. This could be extended by the app server init to include CSIv2 related tagged components, which would remove the need for using the very slow codec APIs.
    3. Registering the ValueFactory instances for the ObjectReferenceTemplate (this is needed so that the ORB knows how to marshal these classes, since their public interface is an abstract value type).
    4. Register the ObjectKeyFactory.
  3. Register the ClientDelegateFactory.
  4. Initialize the transport. As noted in the comments, this is complicated because we support several legacy mechanisms for initialization. The more preferred mechanism for intializing the transport is simply to register all required Acceptor instances (but we need a better framework for creating Acceptors easily, I suspect). But we also have the older SocketFactory mechanism, as well as a number of even older configuration parameters. See sec:Transport-Design for a discussion about the transport design.
  5. Initialize naming. This really means setting up the resolvers for resolve_initial_references and related methods. This provides access to a name service through either the old bootstrap or the standard INS mechanisms. Resolvers are discussed in sec:Resolvers.
  6. Initializer the service context registry. Just as in the IOR case, this could be extended by the app server init to include CSIv2 related service contexts, again avoiding the need for using the codec APIs.
  7. Initialize the request dispatcher registry. This is the central mechanism that ties all of the code together that is needed for invoking and dispatching in the ORB. This includes:
    1. Registering ClientRequestDispatchers and ServerRequestDispatchers.
    2. Registering the special ServerRequestDispatcher used for INS (this one has no object adapter).
    3. Registering the LocalClientRequestDispatchers, which are used for co-located requests. This includes all of the servant caching optimizations. One small note on this: we could extend the optimizations significantly to cache the servant in ALL cases, and then have the POA invalidate the cache when necessary. Currently we assume that we are caching only in the ServantLocator case, and we assume that the ServantLocator always returns the same instance for the same object reference. What we have now is fully effective for the App Server, so there has been little incentive to re-visit this issue.
    4. register the ServerRequestDispatcher used to handle the bootstrap mechanism.
    5. Register the ObjectAdapterFactories.

    Much of this registration is driven by subcontract IDs. See sec:Subcontract-IDsfor more details.

  8. Register the initial reference for dynamic any support.
  9. Handle the persistent server initialization.

2.3.3  Initializing the ORB in the App Server

TBD

portable interceptors

PEORBConfigurator

The ORBManager

2.4  ORB Shutdown

The primary issue in starting up the ORB is simply to configure all of the data needed for running the ORB. In contrast, shutdown must carefully control access to the ORB so that spurious errors do not occur in requests that are in the middle of being processed.

Details TBD.

Chapter 3  Dispatch Path Overview

Here is a simplified sequence diagram describing the overall ORB dispatch path:

(discuss this at a high level)

The next few chapters look at this in more detail through the PEPt model.

Chapter 4  Presentation

The presentation layer is largely concerned with two issues:

The APIs in the first bullet are largely about the stubs on the client side, and the skeletons on the server side. Demultiplexing is the job of the Object adapter (see 8.1).

4.1  Stubs and Skeletons

The IDL to Java mapping and the Java to IDL mappings both define the architecture of stubs and skeletons. The overall stub architecture (static and dynamic) is described the Dynamic RMI-IIOP document.

4.2  Data types

The ORB supports all of the standard OMG data types that are available in the Java language mapping (see 5.3 for the details of the standard types, and their encodings in CDR. Note that CDR is only one possible encoding for the standard types).

Chapter 5  Encoding

5.1  Repository IDs

A repository ID in CORBA is the on-the-wire representation of a type. Several forms are defined in the CORBA spec, and in fact the format is extensible, but we are only interested in two forms: the IDL and the RMI-IIOP repository IDs.

The IDL repository ID has the form IDL:<identifier list>:<major>.<minor>. The identifier list consists of a number of identifiers (typically as are used in IDL module and interface names) separated by “/”. This usually corresponds to the declaration used in the IDL source file.

The RMI repository ID has the form RMI:<class name>:<hash code>:<serialVersionUID>. The serial version UID is used (as usual) to determine whether two classes of the same name really have compatible representations. The hash code is unique to RMI, and allows the CDR protocol to determine whether or not the sender and the receiver have the same version of the class identifier by the class name. If the do, the receiver can use its local class declaration to unmarshal the received data, otherwise the receiver must use a special sending context object to obtain the sender’s version of the class. The receiver then executes an algorithm to match up the data sent with the class in its local environment.

5.2  Buffer Management

Because the ORB uses NIO in its transport, it is necessary to use NIO ByteBuffers for managing the storage underlying the input and output streams. The ORB general uses direct ByteBuffers, which unfortunately are very expensive to create, and prone to lingering instead of being GCed promptly. Consequently the ORB managers most buffers in pools.

5.3  An introduction to CDR

CDR is the standard binary data representation defined in the OMG CORBA standards. Our ORB uses this as its main on-the-wire representation for all communications. CDR is defined in terms of the IDL types, which are not the same as Java types. RMI-IIOP is defined by translating Java types into IDL types, so CDR also applies to RMI-IIOP. CDR primitives are all fixed in length, and aligned according to their size. This was originally done to make unmarshaling easier for 1980’s era RISC CPUs, but makes little sense today.

CDR supports both big endian and little endian byte orderings (and these are the only byte orderings still in common use, since other word oriented computers no longer exist).


Table 5.1: CDR data types
Data type
Encoding
AlignmentSize
Notes
char
 1  
wchar
 11-4
many different encodings
octet
unsigned 8 bit integer
11
similar to java byte, but unsigned
short
BE/LE 2’s complement
22
same as java short
unsigned short
BE/LE unsigned
22
not in java
long
BE/LE 2’s complement
44
same as java int
unsigned long
BE/LE unsigned
44
not in java
long long
BE/LE 2’s complement
88 
unsigned long long
BE/LE unsigned
88 
float
BE/LE IEE 488
44
same as java float
double
BE/LE IEE 488
88
same as java double
long double
BE/LE IEE 488
8-
not in java
boolean
 1  
enum
as an unsigned long
4 
IDL enum
struct
by element
by elementvariable
IDL struct
union
discriminant tag; branch elements
by elementvariable
IDL union
sequence
same as array
  
IDL sequence
array
unsigned long; array elements
4 (for size)variable
IDL array
strings
unsigned long; bytes; 0
4 (for size)variable
IDL: many encodings
fixed point
-
--
IDL (not commonly used)
value type
complex
  
IDL value type, most Java types
typecode
complex
  
IDL typecode
any
complex
  
IDL any
principal
sequence<octet>
  
deprecated
context
sequence<string>
  
IDL context (deprecated)
object reference
    
abstract interface
    
     

We’ll look at the encoding of more complex types in the following sections.

5.3.1  object references

An Object Reference (objref for short) is the CORBA type that represents a remote object. It contains transport endpoint address information, policy information that may affect how remote invocations are handled, and an identity for the remote object that identifies the object within the transport endpoint.

The specific structure of an objref is the IOR, which is represented in IDL as follows (see src/share/classes/org/omg/PortableInterceptor/IOP.idl, plus the code in com.sun.corba.ee.impl.io.IIOPProfileImpl and IIOPProfileTemplateImpl):

struct IOR { string type_id ; sequence<TaggedProfile> profiles ; } const long TAG_INTERNET_IOP = 0 ; struct TaggedProfile { long tag ; sequence<octet> profile_data ; } struct Version { octet major ; octet minor ; } struct TaggedComponent { long tag ; sequence<octet> component_data ; } struct IIOPProfileBody { Version iiop_version ; string host ; unsigned short port ; sequence<octet> object_key ; sequence<TaggedComponent> components ; }

This is not exactly how all of this is declared: in particular, the ORB does not actually define ProfileBody this way. The spec (formal/02-06-01) defines ProfileBody_1_0 and ProfileBody_1_1 in section 15.7.2, but the only difference is the presence of components at the end. The ORB handles this by checking the version, and not allowing a sequence of components if the version is 1.0.

The IOR supports many different possible representations through different kinds of TaggedProfiles. Our ORB only creates IORS that contain a single TaggedProfile with tag TAG_INTERNET_IOP, so the profile_data is always an encapsulation of IIOPProfileBody. However, the ORB does not use the IDL IIOPProfileBody described above. The actual internal Java representation is given in com.sun.corba.ee.impl.ior.iiop.IIOPProfileTemplate. The write method is implemented as follows:

public void write( ObjectKeyTemplate okeyTemplate, ObjectId id, OutputStream os ) { giopVersion.write( os ) ; primary.write( os ) ; OutputStream encapsulatedOS = new EncapsOutputStream( (ORB)os.orb(), ((CDROutputStream)os).isLittleEndian() ) ; okeyTemplate.write( id, encapsulatedOS ) ; EncapsulationUtility.writeOutputStream( encapsulatedOS, os ) ; if (giopVersion.minor() > 0) EncapsulationUtility.writeIdentifiableSequence( this, os ) ; }

The IIOPProfileTemplate extends List<TaggedComponent>. In the ORB representation of IORs, both TaggedComponents and TaggedProfiles are instances of Identifiable, so that a common set of utilities can be used to write them out.

TaggedComponents

5.3.2  typecodes and anys

5.3.3  value types

5.3.4  Exceptions

5.3.5  abstract interfaces

An abstract interface is a type in IDL that can either represent a value type or an object reference. It is frequently used in RMI-IIOP to represent a type whose most derived type is java.lang.Object. The encoding is simple: first a boolean indicating whether the following data is a value type (false) or an object reference (true). This is just an IDL union with a boolean discriminator type.

5.4  encapsulation

There are several places in the GIOP protocol where a marshalled representation of some data type is embedded as a sequence<octet> inside another type. This is referred to as a CDR encapsulation. The representation is simple: first a byte order marker (a single octet) is marshaled to indicate whether the encapsulation is big endian or little endian. Then the rest of the data is marshaled, using the appropriate alignment.

Typically encapsulation is used for the following data types:

Notes and thoughts?

5.5  code sets

5.6  Alternative encodings

The standard encoding that CORBA uses for all messages is called CDR. CDR (like XDR and many others) is a binary encoding that encodes primitive data types and various more complex data types. However, CDR may not always be the best choice, and we have experimented with other sorts of encodings.

One experiment is the Java Serialization for GIOP (JSG) protocol. This is simply the use of Java serialization instead of CDR to encode data types in GIOP messages. This did not turn out to have any performance advantages in our ORB, so we have never made significant use of JSG (and we will probably remove the code at some point).

Another experiment (still in progress) is parallel marshaling. This is intended to explore several key concepts:

The plan is that such techniques will make it possible to achieve order-of-magnitude improvements in marshaling speed, which are simply not possible with current protocols.

5.7  Analyzing Classes and Java Serialization

5.8  The ORB encoding package

Chapter 6  Protocol

6.1  GIOP protocol

The GIOP protocol is the standard protocol used for CORBA in most application. GIOP is intended to be used over any reliable connection-oriented transport, but most typically it is used over TCP/IP, in which case the protocol is typically known as IIOP. Typically IIOP connections are persistent, and may be shared and re-used for many operations. Also, connection usage is asymmetrical: the request initiator opens the connection, and manages connection caching. The request receiver processes requests, and must send any replies on the same connection on which the request was received. Since any CORBA process may both originate requests and handle requests, it is possible (and common) for a receiver of a request to send a request back to the sender. In such cases, a new connection must be used (unless bi-directional GIOP is used, which we do not support).

Each GIOP message starts with a GIOP Message header, defined as follows for GIOP 1.2:

struct Version { octet major ; octet minor ; } ; struct MessageHeader { char magic[4] ; Version GIOP_version ; octet flags ; octet message_type ; octet message_size ; } ;

The fields are used as follows:

6.1.1  Request Message

The Request message consists of a GIOP message header, a Request message header, and a request message body. The request header for GIOP 1.2 (ignoring target address mode) is encoded as:

typedef short AddressingDisposition ; const short KeyAddr = 0 ; const short ProfileAddr = 1 ; const short ReferenceAddr = 2 ; union TargetAddress switch (AddressingDisposition) { case KeyAddr: sequence<octet> object_key ; case ProfileAddr: IOP::TaggedProfile profile ; case ReferenceAddr: IORAddressingInfo ior ; } ; struct RequestHeader { unsigned long request_id ; octet response_flags ; octet reserved[3] ; // The next two fields are the expansion of TargetAddress in the KeyAddr case short // Always KeyAddr for our ORB sequence<octet> object_key ; string operation ; IOP::ServiceContextList service_context ; } ;

The request body starts (for GIOP 1.2) on the next 8-byte aligned octet after the end of the request header. It contains all of the parameters (in declaration order) of the operation. Note that for idl this includes in and inout parameters. For RMI-IIOP this includes all parameters.

6.1.2  Reply Message

Like the request message, the reply message has 3 parts: a GIOP message header, a reply message header, and a reply body. The reply header (for GIOP 1.2) looks like:

enum ReplyStatusType{ NO_EXCEPTION, USER_EXCEPTION, SYSTEM_EXCEPTION, LOCATION_FORWARD, LOCATION_FORWARD_PERM, NEEDS_ADDRESSING_MODE } ; struct ReplyHeader { unsigned long request_id ; ReplyStatusType reply_status ; IOP::ServiceContextList service_context ; } ; struct SystemExceptionReplyBody { string exception_id ; unsigned long minor_code_value ; unsigned long completion_status ; } ;

The request_id is used to match request and reply, and also to find all fragments if the reply is fragmented. service_context contains any relevant information associated with the reply, just as in the request message. This is commonly added using portable interceptors.

The reply_status indicates the meaning of the reply, and also the encoding of the reply body as follows:

6.1.3  CancelRequest Message

A cancel request message is sent by the client to the server to indicate that the request is no longer needed, and the server should stop processing the message (if it is still processing it) and discard resources assocaited with the request. It consists of a GIOP message header, and a cancel request header. The cancel request header contains only an unsigned long representing the request ID.

6.1.4  LocateRequest Message

A locate request is used to determine if an object reference can handle a request, and if not, what object reference can do so. It consists of a GIOP message header followed by a LocateRequest header, which has the following form:

struct LocateRequestHeader { unsigned long request_id ; TargetAddress target ; } ;

Note that the request uses a TargetAddress. Our ORB will only ever send a KeyAddr type here.

Our ORB rarely uses a LocateRequest, because in most cases it is better to first try the request (which can get exactly the same results as a LocateRequest, plus get the actual result) and avoid an extra round trip. We do use the LocateRequest for bootstrapping, such as in the case of resolving an INS URL into an object reference.

6.1.5  LocateReply Message

A locate reply is sent in response to a locate request. It consists of a GIOP message header, a locate reply header, and a locate reply body. The locate reply header is defined as follows:

enum LocateStatusType { UNKNOWN_OBJECT, OBJECT_HERE, OBJECT_FORWARD, OBJECT_FORWARD_PERM, LOC_SYSTEM_EXCEPTION, LOC_NEEDS_ADDRESSING_MODE } ; struct LocateReplyHeader { unsigned long request_id ; LocateStatusType locate_status ; } ;

As usual, the request_id is used for matching request and reply, and for handling fragmentation. The locate_status determine the result of the request as well as the locate reply body contents as follows:

6.1.6  CloseConnection Message

The CloseConnection message indicates that the connection will be closed. It can be sent by either the client or the server. It consists of only the GIOP message header.

6.1.7  MessageError Message

Message error is sent if the connection receives a message that cannot be interpreted for some reason. Basically this means that a message was received (either by the client or the server) that could not be interpreted, and so no subsequent message can be reliably interpreted either (if you can’t recognize the message type, you can’t determine its length, and so all subsequent message framing is lost). About the only action possible in this case is to close the connection, which results in errors on any pending request on the client side. The message consists of only the GIOP message header.

6.1.8  Fragment Message

Fragments are used to deal with long messages. For example, our ORB uses a default fragment size of 4K bytes. But it must be possible to marshaling things like an array of 1 million longs (8 MB roughly in size). The only way to do this is with fragments.

Request, Reply, LocateRequest, and LocateReply messages may all be fragmented. Note that all of these messages have headers AFTER the GIOP message header that start with an IDL long (4 byte) request ID. The Fragment message itself starts with a GIOP message header, followed by a fragment header containing only an IDL long request_id. This means that essentially all GIOP (1.2!) messages start with a 12 byte GIOP header, followed by a 4 byte request ID. In essense, the request ID is “part” of the header, although the header was not defined that way initially. This is useful, because reading the first 16 bytes of a message (ignoring CancelRequest and MessageError, which are special cases) always tells us the type of the message, its size, and its ID. That is all the information needed to fully read the message from the transport, and dispatch it to the appropriate level of the code after the transport.

6.2  Subcontract IDs

The subcontract concept dates back to the early 90s and the work in SunLabs on the Spring project. Spring was a project to design a new operating system, built completely along object-oriented ideas, that used a few powerful concepts to implement the operating system. Some of these ideas included:

The spring OS work is long gone from Sun at this point, but we have often found the subcontract concept to be useful as one layering mechanism in the ORB. It fits into the protocol part of the PEPt architecture.

Our ORB encodes a 4-byte subcontract ID in the object key in every IOR. However, we currently use the same classes (ClientRequestDispatcher on the client side, and CorbaServerRequestDispatcher on the server side) for every subcontract. But we do make good use of subcontracts for LocalClientRequestDispatchers, which support highly effective optimizations for invoking methods on co-located object references (that is, when the remote object reference is invoked inside the same JVM and ORB that created it).

6.2.1  Colocated call optimization

6.3  IORs

6.4  Service Contexts

6.5  GIOP Message Representation in Java

GIOP messages are encoded in a conventional manner in the com.sun.corba.ee.impl.protocol.giopmsgheaders package. Each version of each type of GIOP message is represented as a separate class, with base classes as needed.

Chapter 7  Transport

The transport is responsible for handling the transfer of data to and from endpoints. Connection management is also an important part of the transport, since we most commonly use GIOP as a protocol, and GIOP is connection based. We also include here the logic that is used to decide which of several possible endpoints should be used for a connection.

The client and the server roles in a CORBA request are distinct, but both are event driven: messages are normally received by a selector thread. The client simply needs to get a connection, write the messages to the connection, and wait for a response. The server is event driven: it responds to messages received. The server also contains acceptors, which represent endpoints on which the server listens for new connections,

Chapter 8  Other Aspects of the ORB

8.1  Object Adapters

8.2  The RequestDispatcherRegistry

8.3  Encoding Details

8.4  ORB Logging

8.5  ORB Monitoring

8.6  ORB versioning

8.7  ORBD and Server Activation

8.7.1  current model

8.7.2  ideas for using ORT

8.8  Portable Interceptors

8.9  RMI-IIOP Implementation

8.10  Resolvers

8.11  Name Services

8.12  ORB and App Server Integration

Chapter 9  Utilities

9.1  Fast Object Copying

9.2  Dynamic Code Generation

9.3  Useful utilities

9.4  FSM Framework

9.5  Graph Utilities

9.6  JDK 5 Specific Utilities

9.7  Timing Framework

Chapter 10  Living with our legacy

10.1  Testing Principles

10.2  Benchmarking

10.3  FOLB Support

10.4  HWLB Support

Chapter 11  Compilers

11.1  New rmic iiop backend

11.2  idlj

Chapter 12  Future Directions

12.1  Embedded Languages

12.2  Components

12.3  Fast Marshalling

12.4  Security

include security document here (that I was working on for a while last summer)

12.5  Better handling of Invocation Info


This document was translated from LATEX by HEVEA.