Core Framework Stack

This chapter introduces the objects that form the core of the Enterprise Objects frameworks in WebObjects 5.2. You need to be familiar with these core objects to understand the concepts in the rest of the book.

The core stack is composed of the classes in these packages:

The access layer also contains the classes that make up the adaptor layer. This chapter discusses the key objects in each layer, how the two core layers interact, and how to customize the core Enterprise Objects stack to meet certain application requirements. It is divided into these sections:

Roles

The core Enterprise Objects stack plays many roles. The most important roles are:

The core stack provides a default configuration that should be appropriate for most kinds of applications. Some applications, however, may need to customize the configuration for performance or other reasons. This chapter discusses the default configuration and its common variations.

Important Objects

To understand the key components of the Enterprise Objects stack is to first understand the most important objects in the core stack. They are illustrated in Figure 5-1. A brief description of those objects follows.

Figure 4-1  Default core objects interaction
Default core objects interaction
EOObjectStore (control layer)

An object store is an “intelligent” repository of objects. It is responsible for constructing enterprise objects, registering enterprise objects, servicing enterprise object faults, and saving changes made to objects. You can think of an object store as a workspace for enterprise objects. EOObjectStore is an abstract class that has a number of specific subclasses in the core Enterprise Objects stack.

EOObjectStoreCoordinator (control layer)

A concrete subclass of EOObjectStore, an object store coordinator mediates between multiple object stores both in the control layer and in the access layer. It mediates between any number of editing contexts in the control layer and their associated database contexts (or other object store subclasses) in the access layer.

When multiple cooperating object stores needs to communicate with one another, the object store coordinator mediates this communication. It provides an abstraction to multiple data sources and multiple workspaces that contain data from those sources, allowing objects made up of data from different data sources to work together.

EOCooperatingObjectStore (control layer)

Another subclass of EOObjectStore, the cooperating object store defines the mechanics for object stores that work together to manage data from several distinct data repositories.

When multiple cooperating object stores need to communicate with one another, the object store coordinator mediates this communication. If a cooperating object store determines that certain changes need to be handled by another object store, it asks the object store coordinator to handle the changes (usually by handing them off to another cooperating object store that can handle them). This is an abstract class whose most commonly used concrete subclass is EODatabaseContext.

EOEditingContext (control layer)

Another concrete subclass of EOObjectStore, an editing context is the core object with which you most often interact. Enterprise object instances live within editing contexts, and editing contexts provide the infrastructure that allows enterprise objects to communicate with one another. An editing context is the highest-level object in the core framework stack and it communicates with a database through its lower-level objects.

EODatabaseContext (access layer)

Another concrete subclass of EOObjectStore, a database context is responsible for analyzing the changes made to enterprise objects in editing contexts and determining exactly the changes that need to be made in the database based on the changes in editing contexts. It hands off this list of changes that need to be made in the database (this list is referred to as adaptor operations) to an adaptor channel by way of a database channel. You rarely need to explicitly interact with a database context.

EODatabaseChannel (adaptor sublayer of access layer)

A database channel is used by a database context to communicate with an adaptor channel.

EOAdaptorChannel (adaptor sublayer of access layer)

An adaptor channel is responsible for actually communicating with a data source and sending it commands. However, the operations an adaptor channel performs are always made within the context of a transaction that is managed by an adaptor context object.

How It Stacks Up

How the objects in Important Objects work together is a bit complicated. It gets even more complicated if you customize the core stack. Figure 5-1 shows a simplified overview of the core stack. The interactions between the objects in the core stack are discussed throughout this chapter.

The most important part of Figure 5-1, perhaps, is that all an application’s sessions use the same object store coordinator. This means that multiple users using the same application instance share a connection to the database and share row-level snapshots of data retrieved from each database (because they share the same database context). This has advantages and disadvantages. See Multiple Coordinators and Optimistic Locking for more details.

The Access-Control Divide

One of the most important things to understand when considering the Enterprise Objects core is the relationship between objects in the access layer and objects in the control layer. Fortunately, this part of the Enterprise Objects core is easy to understand: EOObjectStoreCoordinator mediates between the control layer and the access layer. More specifically, it mediates between editing contexts in the control layer and database contexts in the access layer. EOObjectStoreCoordinator is discussed in Object Store Coordinator.

Object Store Coordinator

By default, an application has exactly one object store coordinator, as illustrated in Figure 5-1. An object store coordinator manages possibly many cooperating object stores (usually database contexts, each of which represents a data source), as Figure 5-2 illustrates. This allows individual instances of enterprise objects to include data from multiple data sources and it allows enterprise objects constituted from data in different repositories to interact with one another.

In Figure 5-2, the object store coordinator manages the interactions between enterprise objects that need to communicate both with a MySQL database and an Oracle database. An object store coordinator is by default associated with as many database contexts (cooperating object stores) as the number of distinct data sources the application connects to.

So as illustrated in Figure 5-2, an application that uses two models, each model connecting to a different data source, has one object store coordinator that mediates between two separate cooperating object stores (database contexts). By default, each cooperating object store has its own database channel and its own adaptor channel.

The object store coordinator allows the application to use multiple data sources and also allows the application’s enterprise objects to consist of data from multiple data sources. The object store coordinator manages the logic that is necessary to support these features.

Figure 4-2  One database context per data source
One database context per data source

When to Use Multiple Object Store Coordinators

When you want to provide each session (each user) with its own access layer stack, you configure your application to use multiple object store coordinators. Specifically, you give each session its own object store coordinator. Doing this also instruments concurrency in your application, so it should be done with caution. See Providing Separate Stacks to learn how to provide each session with its own object store coordinator.

If possible, all users of an application should share an object store coordinator. This is the default behavior (that all editing contexts in a particular application instance share the same object store coordinator) and it helps you avoid and deal with update conflicts caused by multiple users editing the same row or rows of data concurrently.

There are reasons, however, why you would want to provide each user with a separate access layer stack. One of the more common reasons is to provide an audit trail in an application. A crucial part of any audit trail is tracking access to data sources. When you provide each user with a separate access layer stack, you can then use different credentials to establish the database connection, provided that you make the connection programmatically. This is discussed in Connecting to a Database in the section Storing in Code.

Why So Many Object Stores?

After taking a look at the default core Enterprise Objects stack, you’re probably wondering why there are so many different kinds of object stores. These objects are all subclasses of EOObjectStore: EOObjectStoreCoordinator, EOCooperatingObjectStore, EODatabaseContext, EOEditingContext. The short answer as to why there are so many kinds of object stores is that the different object stores know different things about enterprise object instances.

An editing context, for example, has no direct knowledge of how to initiate a transaction with a database. A database context, however, does. An editing context, also, has no knowledge that the enterprise objects it holds may be constituted with data from multiple distinct data repositories. An editing context’s object store coordinator, however, knows this and knows how service requests that require managing data from disparate sources.

While you don’t need to understand the complexities of all these types of object stores or how they interact, just know that they each play an important role within the frameworks.

Providing Separate Stacks

To provide each user of an application with an independent access layer stack, you provide each user with a separate object store coordinator. As described in The Access-Control Divide and Object Store Coordinator, an EOObjectStoreCoordinator is the highest-level object in the access layer stack. Each object store coordinator instance, then, represents a single database connection to each of the cooperating object stores registered with it.

The editing contexts that share an object store coordinator share a single database connection. Editing contexts that use different object store coordinators can perform concurrent operations to the same database. So if user A performs a fetch, user B can perform a commit concurrently.

To provide each user with an individual object store coordinator, you instantiate a new EOObjectStoreCoordinator, instantiate a new EOEditingContext whose parent is the new EOObjectStoreCoordinator, and set a session’s default editing context to be the new editing context whose parent is the new object store coordinator. This can be done programmatically in a Session class using the code in Listing 5-1.

Listing 4-1  Providing separate object store coordinators

public class Session extends WOSession {
 
    public Session() {
        super();
 
        /* Provide each session with its own access layer stack. */
        EOObjectStoreCoordinator parentObjectStore = new EOObjectStoreCoordinator();
        EOEditingContext editingContext = new EOEditingContext(parentObjectStore);
        setDefaultEditingContext(editingContext);
    }
}

A common reason to provide each user with a separate stack is to provide each user with a separate connection to the database. If you need to do this, you also need to set values in the connection dictionary for each user. This is discussed in Providing a Connection Dictionary in Code.

The objects in an Enterprise Objects application using a separate object store coordinator per session are illustrated in Figure 5-3.

Figure 4-3  Per-session object store coordinators
Per-session object store coordinators

When you provide separate object store coordinators to each session, you must adopt a different set of assumptions about how the object graph works. When you provide each session with its own access layer stack, you provide each session with a separate EODatabaseContext, which means that sessions no longer share row-level snapshots—they have entirely separate row-level snapshots. Since row-level snapshots are an integral part of Enterprise Object’s optimistic locking mechanism, you must reconsider the locking strategies you implement. This and other related issues are discussed in Multiple Coordinators and Optimistic Locking.

Accessing Lower-Level Objects

Advanced users may need to access an editing context’s access layer or adaptor layer objects. To access an editing context’s access layer objects, you get an editing context’s object store coordinator and the object store coordinator’s database context object. From the database context object, you can get the database context’s adaptor-level objects.

An editing context can have more than one set of access layer and adaptor layer objects, so you need to identify which set you’re looking for. You can do this a number of ways: with a fetch specification, with a global ID, or with an enterprise object. The code in Listing 5-2 uses a fetch specification to help the root object store determine which of its cooperating object stores can service your request.

Listing 4-2  Accessing lower-level objects

EOEditingContext editingContext; //Assume this exists.
EOFetchSpecification fetchSpec;
EOObjectStoreCoordinator rootObjectStore;
EODatabaseContext databaseContext;
EOAdaptor adaptor;
EOAdaptorContext adaptorContext;
 
fetchSpec = new EOFetchSpecification(entityName, null, null);
rootObjectStore = (EOObjectStoreCoordinator)editingContext.rootObjectStore();
databaseContext = (EODatabaseContext)rootObjectStore.objectStoreForFetchSpecification(fetchSpec);
adaptor = databaseContext.database().adaptor();
adaptorContext = databaseContext.adaptorContext();