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.
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.
|