Bob Bretl, Allen Otis, Marc San Soucie, Bruce Schuchardt, R. Venkatesh
GemStone Systems, Inc.
Abstract
The 3 tier architecture logically separates the functions of an application into a user interface
component, a server business logic component, and a database component. Many application
server products, ORBs, and middleware products provide support for building and deploying
applications using the 3 tier architecture. In most of these cases a primary role of the middle tier
business logic components is to manipulate data stored in and accessed from the 3rd tier. The
GemStone/J application server includes a capability for creating, storing, and using persistent
Java objects in the middle tier. We argue that the use of persistent Java objects in the middle tier
of a 3 tier application offers significant advantages over other approaches.
Derivatives trading system - The application is a mathematical modeling system which is used to
simulate and forecast trading based on historical data. The application server retrieves the historical
data, runs the modeling program and updates actual trade information on a 3rd tier database which
contains the official bank records.
Semiconductor fab control - Clients are user interfaces that provide interactive query and update to the
manufacturing process as well as unattended machines which update process data. The application
server maintains accurate work-in-progress, machine scheduling, and historical process control
information for the entire factory. External 3rd tier data sources are accessed to retrieve customer order
information (input to process scheduling) and to update production totals.
Bank information system - The user interface allows a banking officer to view all of a customer’s
accounts in an integrated fashion. The application server caches account state and information about
available products, rates and services and updates the changes on the existing mainframe database.
A scalable application server needs to handle large numbers of clients and access large volumes of 3rd
tier data. This implies many Java virtual machines, many threads per virtual machine, and large object
memory spaces.
2. Overview of GemStone/J
GemStone/J provides an environment for running the middle tier of 3-tier Java and CORBA applications,
offering load-balanced, brokered access to Java processing resources, shared persistent Java objects for
advanced data models, data caching and coordination, managed access to 3rd tier databases, and services
for deploying and managing running systems. A set of client interfaces provide both access to the
application server from a variety of client platforms, offering services from CORBA to interactive,
conversational sessions to scalable transaction management.
This means that creation of new persistent objects requires only these steps:
• Create the object
• Begin a transaction
• Establish a reference to the object from another object which is directly or indirectly connected to a
root object
• Commit the transaction
The first and third steps are carried out with typical Java language operations. The second and fourth are
carried out by using a transaction service API, gemstone.services.GsTransactionService.
Additional transaction services, such as the CORBA Object Transaction Service and declarative
Enterprise Java Beans transactions, will be supported in future releases. Also, it is not necessary for a
Java program running in GemStone/J ever to commit a transaction. In such a case, all objects created by
that program live only as long as the application continues to run. This is standard Java behavior.
Persistent objects committed to the repository outlive the application that created them, and are not
subject to garbage collection by the VM while it is running. If a subsequent transaction causes the last
reference to an object to be removed, the object is a candidate for garbage collection. To reclaim space
taken up by such unconnected objects in the repository, the GemStone/J server includes a background
garbage collector that detects such objects and returns the space they occupy to the repository for reuse.
Changes to persistent objects are made permanent and visible to other applications in the repository only
when a transaction is successfully committed by the application making the changes. Until that time,
those changes are visible only to the application that has made them. This is required to insure that
changes to shared objects leave the repository in a consistent state, and is a key characteristic of ACID
transactions. Otherwise, the integrity of the information enclosed in the objects would be compromised.
Persistence Implementation
GemStone/J implements persistence without changing the Java language or class files by replacing the
object manager in the standard Java VM with an object manager that understands persistence and
transactions. The GemStone/J VM is a fully Java 1.1 compliant VM, passing all of the Java
Compatibility Kit tests. The Java JDK 1.1 API suite is available to every GemStone/J VM, and is pre-
Persistent Java Objects in 3 tier architectures 08/17/98 Page 4
loaded into the default object repository. Java object semantics, Java threads, class initialization, and
object finalization all function as specified by the Java 1.1 platform specifications.
The GemStone/J object manager, and its associated garbage collection mechanisms, make use of
operating system shared memory and a number of support processes to implement concurrent access to
the shared object domain from a large number of threads and VMs. The object management algorithms
use shared memory both for shared object access, swapping objects between disk and memory as needed.
This allows a GemStone/J VM to operate on a very large domain of objects – over a billion objects -
without consuming large amounts of private memory. It also provides support for efficient use of very
large objects - images, video, sound samples, et cetera. Use of shared memory and the disk is transparent
to the application and to the developer, and is visible only indirectly to the system manager.
VM processes, shared memory caches, repository files, and system processes can be transparently
distributed to as many heterogeneous machines as needed for optimal performance and maximum
leverage of resources. SMP architectures are automatically utilized. Changes to objects are recorded in
transaction logs that allow for point-in-time recovery in the event of hardware failure. Transaction logs
and repository files can be mirrored to standby machines for safety. Backups of the repository are
performed on-line, and an on-line persistent object garbage collector insures that space freed by removal
of objects is made available for reuse. The server provides real-time performance statistics gathering,
which can be used to implement configuration tuning changes while the system is running.
Transactions and locking: GemStone/J transactions are controlled by the application (or the Enterprise
JavaBeans container) by use of a transaction service API, which provides begin/commit/abort methods as
well as a variety of transaction inquiry and support methods. A lock service provides mechanisms to
establish and inquire about object-level locks used to insure successful transactions in concurrent use
situations.
Login and authentication: GemStone/J provides object-level security for persistent objects. As a result,
it is necessary for the application or user to provide a login user name and password to obtain access to
the object repository. Once provided, the VM insures that only allowed object accesses are permitted.
Class loading: GemStone/J can store Java classes and methods in the shared repository. This allows
classes to be loaded from shared memory instead of from the file system through a class path. The use of
shared classes is integrated into the standard class lookup scheme, so that developers and application
code need not be aware of the mechanism. Classes can be accessed from the filesystem as usual, or can
be stored in the repository, from which they will be loaded transparently at runtime.
Persistent Java Objects in 3 tier architectures 08/17/98 Page 5
Naming Objects: GemStone/J provides NameService APIs that allow binding, resolving, and unbinding
objects. Binding an object makes it a named persistent object as of the next transaction commit.
Unbinding makes the object potentially garbage collectable as of the next transaction commit. Both
global and per-user-account name spaces are provided.
Scalable Containers: GemStone/J provides a number of scalable container classes for building large
collections of Java objects. These containers grow dynamically to very large sizes without significant
degradation in performance of common operations such as addition, removal and enumeration. Java
applications do not have to request dynamic growth, and do not have to code around size limitations.
Concurrent Update Objects: Concurrent update objects are designed to provide a higher degree of
concurrency than conventional objects in multi-user update situations. They provide protocol for
common update operations for which locks are not required, even if multiple applications are
concurrently performing updates. Their behavior in transactions is governed by logical concurrency rules
rather than physical read-write conflicts. They greatly simplify the task of designing multi-user
applications. CuQueue, for example, provides a multiple producer, single consumer model of queue
management, which does not require any locking. CuIdentityBag allows multiple applications to
concurrently add elements or remove different elements.
Changed Object Notification: Applications can request notification when other applications or users
commit changes to persistent objects of interest.
Two-phase commit: Two phase commit support is in development and will be included with the release
of EJB functionality. GemStone/J will be able to function as a resource participating in transactions
using the CORBA Object Transaction Service, the Java Transaction Service, or as an X/Open XA
resource.
Scaling the number of clients that can be simultaneously served requires scaling the number of threads
the server can run. There are limits to the number of threads within a single Java virtual machine, based
on operating system limits on number of threads per process, and practical limits imposed by thread
management within the virtual machine. In addition, engaging more than one piece of hardware will
require use of more than one Java VM. This means that a highly scalable middle tier Java server needs to
combine a large number of VMs with a substantial number of threads in each VM. GemStone/J does this.
Scaling the size of the server object space is affected by the limits of garbage collecting large memory
spaces. Also, a purely memory based object manager is probably not extendible outside the boundaries
of one shared memory machine. At some point a large amount of cached data in the server will also run
Persistent Java Objects in 3 tier architectures 08/17/98 Page 6
into restart issues. The time to load or initialize large object caches may become excessive, if those
caches are not made persistent and made quickly recoverable by transaction logging techniques.
The persistent storage manager is able to efficiently manage disk I/O to a large object space on disk, with
just the working set cached in memory. A purely virtual memory based approach to a large object space
may have problems, such as excessive I/O caused by a garbage collector that assumes all objects fit in a
resident set of virtual memory pages. Making cached data persistent within the middle tier allows
garbage collection of long lived objects to be separated from garbage collection of short lived objects,
thus allowing more flexibility in garbage collection algorithms, and scheduling of garbage collection for
off-peak hours of the day.
By including a shared memory cache, as shown in Figure 3, GemStone/J allows multiple virtual
machines running on one shared memory node to efficiently access a large persistent store, and to utilize
multiple CPUs . The shared cache allows disk I/O for commonly accessed objects to be performed by one
virtual machine and then the resulting buffers in the working set can be used by other virtual machines.
Server applications typically access a large amount of information. Unless all server threads are running
in the same Java VM and care is taken to provide shared access to this information the memory
consumed by each server’s redundant information is quite costly. Trying to control shared information in
this way limits the servers to a single VM and its inherent resource limits, and adds complexity to the
architecture of the system. Pushing the management of shared information into the persistence service
itself, by means of a shared object cache located in shared memory, reduces application complexity while
enabling any number of Java VMs access to the objects in the cache. Having the persistence service
itself manage caching of shared information also provides server applications with the ability to perform
transactional updates on the information, an operation more difficult to achieve when sharing is done at
the application architecture level.
Persistent Java Objects in 3 tier architectures 08/17/98 Page 7
GemStone/J External
Data
Servers
CORBA ORB
ORB Java VM
Client IIOP
Java/C++
Java VM
Web Service JDBC
IIOP
GemStone/J VMs
Web Server
Java Beans
Client
Transaction
Log Transaction
Monitor
Clients -
Web Browsers or
Java VMs Garbage
Extents Collector
Java VM Java VM
Java VM
Garbage
collector Monitor
Java VM Java VM
Page I/O
Monitor
Shared Cache
Shared Cache
G.C.
modeling, development, and performance advantages, the use of persistent business objects is still a
small part of mainstream commercial application development.
Nonetheless, persistent objects can provide significant benefits to applications written in object
languages, even if those applications are not storing and managing primary business data as persistent
objects. The following sections describe some of the ways in which persistent objects managed by a
middle tier application server provide benefits to applications whose main business is manipulating other
forms of data.
• For the application developer, use of a persistent cache simplifies the programming of data
manipulation substantially. Instead of coding to SQL, JDBC, and/or an object/relational mapping
service, the developer codes Java, and lets the caching mechanisms manage the database interactions.
Persistent Java Objects in 3 tier architectures 08/17/98 Page 10
• Control objects will survive system failures and GemStone/J recovery or restore operations. This
makes the application runtime environment fault tolerant. This is particularly valuable for persistent
queues, which require restarting from a known state to insure proper sequencing.
• GemStone/J's ability to dynamically update persistent Java object structures allows a new version of
an application – with new control object classes – to be installed without sacrificing existing control
objects.
being modeled and stored as persistent objects. The application in these cases gathers, presents, and
manipulates 3rd tier data alongside new information represented as persistent objects. Transactions may
be separate, if the two classes of information are modified separately by the application, or coordinated in
distributed transactions if single application operations update both kinds of information.
The reasons for doing this may be several. Often the information in question is complex, with a complex
Java object model that may be difficult to express in regular or normalized data management systems,
and which may be expensive to decompose for storage and reconstitute for access. Storing the
information as objects can make it substantially easier to change the object model later, reducing the cost
of application change, and allowing for greater flexibility in responding to business changes. Also,
designers of new applications designed around distributed Java technology and incorporating a variety of
heterogeneous data sources and applications may find that the reduced complexity of using persistent
objects can mean the difference between manageability and unmanageability of the result.
• GemStone/J’s ability to run 3rd party Java class libraries and make their instances persistent means
that overall the application developer can reuse a great deal more code than would be possible if all
instances had to be persisted to a relational or mainframe database.
Such an implementation of the NS works with Java objects only and need not perform queries against an
external database. The performance of the NS is dramatically improved while maintaining the durability
and transactional nature of its directory. We have found that most ORB implementations do share a
common shortcoming regarding persistence in that the actual reference information in stubs is held in a
transient variable. The NS has to provide a simple workaround for this by storing the reference
information (typically an IOR object) as additional information in a binding.
5.1.2 Interface Repository
An ORB typically makes an Interface Repository (IR) available to applications to allow the use of
dynamic invocation of operations from a client, and dynamic dispatching of operations in a generalized
skeleton in the server.
While the IR is typically heavily updated during development time it is also typically read-only in a
deployed application. Developers require it to be a shared database with ACID transactions while
deployed applications require very fast read-only queries. Making the IR a persistent object in
GemStone/J satisfies both these needs since persistent objects are also naturally transactional.
At runtime, applications get maximum query performance as the attributes of this IR’s object-oriented
schema can be queried in a natural fashion and the resulting information is already in object form. A
persistent IR can also be used to simplify an ORB by blurring the edges between static and dynamic
invocation methods, as is demonstrated in GemStone’s GemORB Smalltalk ORB. In it the IR is used
during all dispatching by the ORB itself, requiring the implementation classes to implement a one-line
method that identifies the CORBA types supported by instances of the class. This removes the need for
skeletons or TIE objects in the ORB altogether, reducing the size of a CORBA application at runtime
significantly while introducing minimal dispatching overhead. The shared object cache in GemStone
allows each server process to directly access the IR in a shared memory node, allowing this kind of
dispatching to be done with minimal disk access.
The use of segmented object identifiers to delegate location of object implementations to persistent
object managers has been demonstrated in the VisiBroker for Java ORB, which is integrated with the
GemStone/J version 1.1 product. While segmented object identifiers can be used to locate information
needed to compose objects in an object-relational mapping system, it is easier to directly map object keys
to persistent object identifiers, or to a persistent name space. Using the VisiBroker Activator service, a
Persistent Java Objects in 3 tier architectures 08/17/98 Page 13
precursor to the new POA servant managers, we identify the Activator service with the initial portion of
the object identifier. The Activator service then uses the remaining portion of the key to look up the
object in the persistent object store. In one implementation this was done by using the persistent object’s
object identifier, embedding the OID in the CORBA object reference when it was formed and given to
clients and using this OID to look up the object in the persistent store directly (a one-step query on the
persistent object manager in GemStone/J). In another implementation the Activator service used a key
from a persistent naming service in GemStone/J to form the CORBA object reference that was given to
clients, and then later used this key in locating the persistent object.
Since persistence is by reachability and not by inheritance in GemStone/J, and since no coding need be
done to make an object persistent, this approach to delivering persistent objects to the CORBA bus can
be used for any CORBA-enabled object. Further, the shared memory architecture of GemStone/J allows
the exact same CORBA object to be serving in multiple Java VMs simultaneously, allowing higher
throughput for CORBA.
Enterprise JavaBeans architectures can benefit from persistent, transactional object services in the same
way as CORBA architectures. EJB places the additional demand on the persistence mechanism that it
support two phase commit if it is to be used for anything but storage of EJB meta-data.
EntityBeans can benefit further if the Enterprise Beans themselves can be stored as persistent,
transactional objects. Object reference segmentation can be used, as with CORBA, to quickly locate an
EJB Home for a bean, and the EJB Home then has direct reference to its persistent EntityBeans.
Persistent Java Objects in 3 tier architectures 08/17/98 Page 14
7. Summary
We have described how the ability to use persistent Java objects can provide significant benefits to
application developers and to the performance and manageability of 3 tier applications. Many of these
benefits accrue to the infrastructure of the application server, and so to the applications that run in it,
even though many of those applications do not make explicit use of persistent Java objects. Applications
that do make explicit use of persistent Java objects can reap still more benefits, notably in the simplicity
of data modeling and in performance.
References
1. The Essential Client Server Survival Guide, Orfali, Harkey & Edwards, ISBN 0-471-15325-7 .
4. GemStone/J 1.1 product. Trial copy with html documentation available at www.gemstone.com .