developing Enterprise components
 

EJB Container Services


First realize that beans are intended to be encapsulated reusable components, much like electronic "chips". The "encapsulated" adjective means that their internal arrangment is none of your business, and you can go by the external specification of the behavior of the component, at the base of which is the "API" (Application Programming Interface) of the component.

In the case of Java this API is usually exemplied by a set of interfaces, which by definition are independent of their implementation.

In the case of the inner workings of the bean, the implementation of the business methods is delivered by the bean provider, while the implementation of the dialogue with client and container is provided by the container.

This means that clients can be written that will work with any implementation of the component and container, which on the server side translates to meaning that the server administrator is free to substitute new implementations whenever desired.

It also means that the same components can operate in very different server environments, which allows for great flexibility in terms of pooling of resources, redirection of queries according to load, and failover involving very inhomogeneous collections of servers.

If you say this is not important to us, we are not in the business of designing components for others to use or even for ourselves to use in multiple projects [you will be wrong and] you may be ignoring a very common reason why projects have a short lifetime, the change of hardware/operating system technologies. It is nice if you do not have to redo a project just because you wish to change the server-side boxes: or, more likely, if you do not have to stick with particular servers because of the fear of disruption such a change would cause.

An important part of the argument for reusability is the experience we gain from multiple uses of the same component, and the familiarity we gain which can be leveraged to improve our designs. Equally significant is the fact that components (or specifications for components) can be developed across an industry, so that one set of clients can be used in dealing with multiple companies.

Concepts

There are five concepts whose implementation is provided by a cooperative arrangment of component and container, controlled by the deployment descriptor.

^ Persistence

Some of our data should exist before the client comes along, and should exist, perhaps with some of its properties (such as quantity on hand for an inventoried item) changed after the client departs.

In planning a complex trip we begin before a particular query arrives with information about flights and rooms and automobiles available on particular dates, and end (if planning succeeds) with reservations made, resulting in a decrease of the numbers of those resources available, and tickets issued. These are part of the state of the travel enterprise.

Other data relates to the specifics of the client query, may be modified while processing that query, and may not be saved after the reply to the query is generated. That data did not represent permanent changes to the state of the enterprise.

It will be useful if the client programs, and the components that process the queries from those client programs, do not have to build in details about the way in which persistence will be done. This follows the general component philosophy discussed above. You may wish to change the way in which persistence is done, you may want to use the same component on servers that do this persistence in different ways, and there may be multiple providers for a services, or very different providers for the different parts of the transaction. And of course you do not want to limit the forms of provider with which your component can be used, you would rather "plug this in" at a particular location as the component is installed.

So in the interface and deployment descriptors you describe what needs to be persisted, meaning what needs to exist when the query arrives and after the reply is delivered, and leave it to the server vendor to make sure that this "in-between" magic occurs.

^ Queueing

This is not dealt with by a user specifically, but during the design process we need to be aware that it will be going on, so that we do not have expectations that will not be met by the server-side architecture.

Ability to perform queueing (under some name which includes the term "pooling") is vital in achieving the efficiencies that make possible the large-scale deployment of components across an enterprise.

A familar example is a "database connection pool". A connection to a database is a very valuable resource as far as the database system is concerned, and creating and destroying such a connection can be an expensive task. Typically each transaction across a connection is independent, which means that one connection could be used for tasks from many differnent problems. Various clients could ask for the momentary use of a connection long enough to do a particular QR-transaction, and while waiting for that client to decide on another query could be reused by many other clients.

Similarly there may be many clients that use a particular component. Creating an instance of a component can be expensive, and it would be nice if an instance could be used multiple times after it is created.

The first stab at this uses the "multiple-I" set of concepts. The instance is an "instance of an implementation of an interface", and has a particular state at any instance based on its "identity". Perhaps we can find inexpensive ways of "switching identity" (typically just by reassigning values of the fields that determine the state, based on that identity), and then there can be an "instance pool" for the component, just like the connection pool, and while a client is not using the instance many other clients can make use of it, each temporarily being provided with the proper state.

This means again that the methods we program are wrapped by the container with methods that make sure the proper state is supplied to the component before the method on it is called, and make sure that the state is saved in some form after the method call has returned, where "saved" in this context does not mean "persisted as part of the enterprise base of data", but rather "saved so it can be restored when the client needs the instance again during its current processing".

^ Reference

identity

There are a number of issues related to the "identity" of an object.

In discussing Queueing we saw that during a transaction there is an identity that specifies the state of an object (that is, of an instantiation of an implementation of the object interface). This identity is only operative during a particular transaction, and is not independent of the client.

In EJB there is a SessionBean. This kind of EnterpriseJavaBean has an identity determined by its "isEqual()" method. In other words you can always ask a SessionBean instance if it is "equal" to another SessionBean instance.

There are in fact two kinds of SessionBean. If it is "stateless" then all instances are to be considered alike, and "isEqual()" returns "true" if a SessionBean is an instance of the same class as the bean it is compared with. If it is "stateful" then two instances are considered to be alike precisely when they say they are alike, that is, the implementation of "isEqual" will determine their identity.

It need not be the case that the two instances are actually alike in that they represent the same reference inside the Java Virtual Machine, or even that all of their fields have the same values. It is quite possible that there are transient values, useful during the processing but not part of the state of the instance. In fact for stateless beans no field values are part of the identity.

Hopefully the preceding leaves you slightly confused. Why do we have fields at all if their values do not matter? Well, this relates to when one instances might be substituted for another. This will not happen during the invocation of one of the methods of the bean: so state is something that occurs "in between" invocations. In fact one way to describe a "stateless" bean is to say that nothing persists between invocations.

With persistence we saw the need for another form of identity, in this case one that is independent of any particular client. This form of identity relates to the permanent base of data of the enterprise.

In EJB there is a particular kind of bean called the EntityBean. This kind of EnterpriseJavaBean has a "primary key class", and instances of this class are precisely the things that determine the identity of the particular instance. In other words two instance of an EntityBean that have the same value for their primary key class are actually to be treated as the same instance.

Typically a client begins a query by asking for an instance of a particular SessionBean, or perhaps instances of several of them. This instance is then given an appropriate identity, not from values provided by the client, but from values created by and recognized by the container.

Inside the query a client may refer, usually through a SessionBean, to an instance of an EntityBean. This reference is specified by giving a value for the primary key (or values from which this primary key can be constructed), and then an instance is supplied by the container with all the proper field values in place. If changes are made to persistent fields of this instance they now become part of the persistent state of the instance identified by that primary key [the statement says a lot, and much more needs to be understood, but the idea is correct].

naming

Separate to some degree from the issue of identity is the issue of naming.

At first thought we can say that at least if two objects have the same name then they should be absolutely identical. After reviewing the concept of queueing we may decide that this first thought, like many, had a kernel of truth, but is not the whole truth.

Objects with the same name must have the same behavior, and be in the same states at the same time, and that is their form of identity.

Names in EJB are used to "find" objects rather than to determine their identity. Full use is made of the JNDI (Java Naming and Directory) API.

Every bean specifies a name under which its home interface is found. A client of that bean then creates an instance of the home interface, and that for an instance of the bean.

  • If it is a session bean we then just ask for an instance of the bean itself, and one is created (or as we now know a pooled instance is assigned to us).
  • If it is an entity bean we ask the home to find it by its primary key, or by method related to its data values.

^ Security

Here we have the usual games, played in a new way. The "owner" of the bean may want to control who can access it in various ways, and the identity of the "user" of the bean may be determined in various ways.

EJB uses "role-based security". Your design contemplates a "role". Your client is identified by the container as a particular "principal". The rule is that only a principal which is in the contemplated role may perform a particular access.

The container (using tools appropriate for the particular server) looks at whatever is known about a particular client, and assigns it to be a particular instance of the java.security.Principal class.

The deployer determines which role can be implemented by each instance of java.security.Principal.

This allows the bean provider to determine abstractly exactly "who can do what", and allows the bean deployer to determine the "who" for a particular client in accorandance with the administrative practices of the deploying organization and the tools available on the particular server.

^ Transaction

The idea (as distinguished from the implementation) of transactions is simple. An enterprise can exist in various states. A transaction is intended to move the enterprise from one legitimate state into another legitimate state. Unfortunately for all of us this cannot be done as it has been done for years on "Star Trek", it has to be done piece by piece, and any particular piece can fail.

So a transaction typically tries to move from some legitimate initial state into a legitimate final state. It does this by going through intermediate states of the EJB system, most of which may not be legitimate as states of the enterprise (e.g. a ticket is created for a flight on an airplane, but payment from the customer has not yet been received). The container must arrange (with the aid perhaps of mountains of external software) that if any intermediate transition cannot be performed, the whole thing will end up as if it had never been requested. In other words we guarantee that at the end either the system is in the original initial state, or the desired final state, and not in some strange state in between.