Errata

Enterprise JavaBeans

Errata for Enterprise JavaBeans

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released. If the error was corrected in a later version or reprint the date of the correction will be displayed in the column titled "Date Corrected".

The following errata were submitted by our customers and approved as valid errors by the author or editor.

Color key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted By Date submitted Date corrected
Printed
Page ix
The following entry has been added to the Table of Contents

D. EJB 1.1: The Next Version

Anonymous    Aug 01, 1999
Printed
Page xiv
Added this description of Appendix D

Appendix D, EJB 1.1: The Next Version

This appendix provides a summary of changes between EJB 1.0 to EJB 1.1
and a strategy for mitigating portability problems between versions.

Anonymous    Jan 01, 2000
Printed
Page 8
In about the middle of the page, changed

"In a read application, we would..."

To:

"In a real application, we would "

Anonymous    Dec 01, 1999
Printed
Page 25
Line 6 of the second paragraph read

An entity bean must extend javax.ejb.EntityBean; a session
bean must extend javax.ejb.SessionBean.

It now reads:

An entity bean must implement javax.ejb.EntityBean; a session
bean must implement javax.ejb.SessionBean.

Anonymous    Aug 01, 1999
Printed
Page 28
Toward the bottom of the page, changed

"notify the bean class that it is about activated "
to:

"notify the bean instance that it is about to be activated "

Anonymous    Dec 01, 1999
Printed
Page 59
The example read

public HypotheticalBean extends EntityBean {
public int x;
public double foo() {
int i = this.getX( );
return this.bar( );
}
public int getX(){
return x;
}
private double boo(int i){
double value = x * x;
return x;
}
}

It now reads:

public HypotheticalBean extends EntityBean {
public int x;

public double foo() {
int i = this.getX();
return this.boo(i);
}
public int getX(){
return x;
}
private double boo(int i){
double value = i * Math.PI;
return value;
}
}

Anonymous    Dec 01, 1999
Printed
Page 73
In the lowest boxin the figure, the method name now reads

"setDeckLevel()"

instead of:

"setName()".

Anonymous    Dec 01, 1999
Printed
Page 84

The first line of second paragraph started with
The CabinBean class extends javax.ejb.EntityBean

It now reads:

The CabinBean class implements javax.ejb.EntityBean

Anonymous    Dec 01, 1999
Printed
Page 104
The code line

sd.setSessionTimeout(60);

Now reads:

sd.setSessionTimeout(300);

Anonymous    Dec 01, 1999
Printed
Page 114
The last line of code that read

java.rmi.NoSuchObjectException: Bean has been deleted

was deleted.

Anonymous    Dec 01, 1999
Printed
Page 119
The code line

TravelAgent agent = home.fin;

Now reads:

TravelAgent agent = home.create( );

Anonymous    Dec 01, 1999
Printed
Page 131

Now reads "these method behave"
Should be: "these methods behave"

Anonymous   
Printed
Page 151
The following code block

if(result.next()){
name = result.getString("name");
capacity = result.getInt("capacity");
tonnage = result.getDouble("tonnage");
}else{
throw new ObjectNotFoundException(
"Cannot find Ship with id = "+id);
}

Now reads (the new line is: this.id = id;):

if(result.next()){
this.id = id;
name = result.getString("name");
capacity = result.getInt("capacity");
tonnage = result.getDouble("tonnage");
}else{
throw new ObjectNotFoundException(
"Cannot find Ship with id = "+id);
}

Anonymous    Dec 01, 1999
Printed
Page 168
Sentence reads

"before making itself available to he client."

Should be

"before making itself available to the client."

Anonymous   
Printed
Page 170
Title read

Transitioning to the Ready state from the Pooled state
via passivation

now reads:

Transitioning from the Ready state to the Pooled state
via passivation

Anonymous    Dec 01, 1999
Printed
Page 170
Title read

Transitioning to the Ready state from the Pooled state
via removal

It now reads:

Transitioning from the Ready state to the Pooled state

Anonymous    Dec 01, 1999
Printed
Page 171
In the third line from the bottom instead of

-just before the container reads the container-managed fields.

It should be for more clarity

- just before the container reads writes the container-managed
fields to the database.

Anonymous   
Printed
Page 179
The third line of code read

"import com.titan.travelagent.CreditCard;

It has been deleted.

Anonymous    Dec 01, 1999
Printed
Page 185

The following line of code was deleted
Statement stmt = null;

Anonymous    Dec 01, 1999
Printed
Page 185

code fragment // concatenation of strings was required because of margin size in book.

ps = con.prepareStatement
("INSERT INTO payment (customer_id, amount, type,"+
check_bar_code,check_number,credit_number,
credit_exp_date)"+ VALUES (?,?,?,?,?,?,?)");

now reads (string concatenate fixed):

ps = con.prepareStatement
("INSERT INTO payment (customer_id, amount, type,"+
"check_bar_code,check_number,credit_number,"+
"credit_exp_date) VALUES (?,?,?,?,?,?,?)");

Anonymous    Dec 01, 1999
Printed
Page 194 & 195
Replaced

package com.titan.travelagent;
import java.rmi.RemoteException;
import javax.ejb.FinderException;
import com.titan.cruise.Cruise;
import com.titan.customer.Customer;

public interface TravelAgent extends javax.ejb.EJBObject {
public void setCruiseID(int cruise) throws RemoteException;
public int getCruiseID() throws RemoteException;
public void setCabinID(int cabin) throws RemoteException;
public int getCabinID() throws RemoteException;
public Ticket bookPassage(CreditCard card, double price)
throws RemoteException,IncompleteConversationalState,
DoubleBookingException;
}
------
with:
--------
package com.titan.travelagent;

import java.rmi.RemoteException;
import javax.ejb.FinderException;
import com.titan.cruise.Cruise;
import com.titan.customer.Customer;
import com.titan.processpayment.CreditCard;

public interface TravelAgent extends javax.ejb.EJBObject {

public void setCruiseID(int cruise) throws RemoteException,
FinderException;
public int getCruiseID() throws RemoteException;

public void setCabinID(int cabin) throws RemoteException,
FinderException;
public int getCabinID() throws RemoteException;

public int getCustomerID( ) throws RemoteException;

public Ticket bookPassage(CreditCard card, double price)
throws RemoteException,IncompleteConversationalState,
DoubleBookingException;

}

Anonymous    Dec 01, 1999
Printed
Page 198
Changed

"double.valueOf("
to:

"Double.valueOf("

Anonymous    Dec 01, 1999
Printed
Page 199
The source code read

"public int getCustomerID( )throws RemoteException{
if(cruise == null)
throw new RemoteException();
return ((CustomerPK)customer.getPrimaryKey()).id;
}"

It now reads:

"public int getCustomerID( )throws RemoteException{
if(customer == null)
throw new RemoteException();
return ((CustomerPK)customer.getPrimaryKey()).id;
}"

Anonymous    Dec 01, 1999
Printed
Page 209
Near the bottom of the page, changed

"data, we wou212

ld use the Reservation bean."

to:

"data, we would use the Reservation bean."

Anonymous    Dec 01, 1999
Printed
Page 209
Replaced

public interface TravelAgent extends javax.ejb.EJBObject {

public void setCruiseID(int cruise) throws RemoteException;
public int getCruiseID() throws RemoteException;

---
with
---
public interface TravelAgent extends javax.ejb.EJBObject {

public void setCruiseID(int cruise) throws RemoteException,
FinderException;
public int getCruiseID() throws RemoteException;

---

Anonymous    Dec 01, 1999
Printed
Page 210
Replaced

public void setCabinID(int cabin) throws RemoteException;
public int getCabinID() throws RemoteException;

public Ticket bookPassage(CreditCard card, double price)
throws RemoteException,IncompleteConversationalState,
DoubleBookingException;

public String [] listAvailableCabins(int bedCount)
throws RemoteException, IncompleteConversationalState;
}
---
with
---
public void setCabinID(int cabin) throws RemoteException,
FinderException;
public int getCabinID() throws RemoteException;
public int getCustomerID( ) throws RemoteException;
public Ticket bookPassage(CreditCard card, double price)
throws RemoteException,IncompleteConversationalState,
DoubleBookingException;

public String [] listAvailableCabins(int bedCount)
throws RemoteException, IncompleteConversationalState;
}

Anonymous    Dec 01, 1999
Printed
Page 273
In the first paragraph,

"When the ShipBean is persisted ..."

now reads:

"When the CabinBean is persisted ..."

Anonymous    Dec 01, 1999
Printed
Page 280
In the footnote, changed

"Version 2.0 of EJB."
to:

"Version 1.1 of EJB."

Anonymous    Dec 01, 1999
Printed
Page 306-314

A new appendix, Appendix D, EJB 1.1: The Next Version, has been added. It reads as follows:

On May 12, 1999 Sun Microsystems released the Enterprise JavaBeans 1.1
draft specification (EJB Moscone), the next version of EJB. Enterprise
JavaBeans 1.1 is, in many ways, a point release with corrections and
clarifications that will allow vendors and bean developers to create
more portable beans. However, EJB 1.1 also includes some fairly
important changes that developers will need to understand if they plan
to develop EJB 1.1 systems. This appendix discusses some of the changes
proposed in the draft EJB 1.1 specification, focusing on the most
important and visible modifications since EJB 1.0.

The biggest changes between EJB 1.0 and EJB 1.1 include: mandating
Entity bean support, the adoption of XML deployment descriptors, the
creation of a default JNDI context, and changes to security. Changes to
these areas are addressed below, but be warned: This material is based
on a draft of EJB 1.1, which is subject to modification before its
final release.

Entity Beans

EJB 1.1 mandates support for the Entity bean type. In EJB 1.0, entity
bean support is optional, which means vendors can support them in
whole, in part, or not at all. Most EJB server vendors chose to support
entity beans in some way; for these vendors, the transition to full
support shouldn't be difficult. For most EJB developers, the required
support for Entity beans is welcomed, because it provides a more stable
platform for portable beans.

The Entity bean type itself has undergone some changes. The transaction
attribute TX_BEAN_MANAGED has been removed from entity beans. This
attribute is difficult to use because it requires explicit
transactional control by the developer. Removing it from entity beans
simplifies the EJB architecture. Stateful session beans, however, still
retain the TX_BEAN_MANAGED option.

Another welcome change is the expansion of valid return types from the
find methods for entity beans. In EJB 1.0, find methods can return a
single entity or a collection of entities. Find methods that return a
single entity return the entity's remote interface type; entities that
return a collection use java.util.Enumeration. In EJB 1.1 a new return
type has been added, java.util.Collection. This addition provides both
the vendors and developers with more flexibility in how the find
methods are implemented and used.

A seemingly minor change to the return value of ejbCreate() may turn
out to be a headache when upgrading systems from EJB 1.0 to the EJB 1.1
specification. Because the ejbCreate() method works differently in
bean-managed and container-managed beans, EJB 1.0 specified different
return values: bean-managed entities return the unique identity of the
bean, the primary key; container-managed entities return void. The
following code shows the different method signatures used for
container-managed and bean-managed ejbCreate() methods in EJB 1.0.

// container-managed entity, EJB 1.0
public class AccountCMP implements javax.ejb.EntityBean {
public int id;
public double balance;

public void ejbCreate(int myID) {
id = myID;
}
// more bean code follows
}

// bean-managed entity, EJB 1.0
public class AccountBMP implements javax.ejb.EntityBean {
public int id;
public double balance;

public AccountPK ejbCreate(int myID){
id = myID;
// do a database insert using JDBC
AccountPK pk = new AccountPK(myID);
return pk;
}
// more bean code follows
}

The EJB 1.1 specification changes this, so that both bean-managed and
container-managed entities have to return the primary key type from the
ejbCreate() methods. However, container-managed beans are required to
return null instead of a primary key object. This seemingly bizarre
change was made to accommodate EJB vendors who want to support
container-managed beans by extending them with generated bean-managed
classes. The generated subclasses override the ejbCreate() methods to
manually insert a record into the database. In EJB 1.0, the ejbCreate()
methods in bean-managed and container-managed entities have different
return values, so extending the class and overriding the ejbCreate()
methods doesn't work. By specifying that the ejbCreate() methods must
always return the primary key class, container-managed beans can be
extended to create bean-managed beans. Unfortunately, this change
breaks forward compatibility with EJB 1.0 container-managed beans,
forcing bean developers to make changes to their existing code if they
want to transition to the EJB 1.1 specification.

The specification also contains some other changes regarding the
primary key class. For bean-managed entity beans, EJB 1.1 states that
the primary key class can be any valid Java RMI-IIOP type-a clear
indication that IIOP will be the standard distributed object protocol
for EJB in the future. In addition, the new specification requires the
primary key class to implement the Object.equals() and
Object.hashCode() methods to ensure that these methods evaluate
properly when comparing keys and storing them in a java.util.Hashtable.
The most significant change regarding primary keys is the option to
defer their definition until deployment time. In other words, the
primary key for an entity bean doesn't have to be defined by the
developer, but can be left to the deployer. This is a significant
departure from the previous specification, which required the bean
developer to define the primary keys. By deferring the definition until
deployment, persistence mapping becomes more flexible, allowing beans
to become more portable. Although this is a convenient option, it's
likely that most bean developers will continue to specify the primary
class when they develop the bean.

Finally, a change in the EJB 1.1 specification allows entity bean
references to be container-managed fields. In container-managed
entities the container manages persistence automatically, so it must be
told at deployment time which fields are persistent and how to map them
to the database. In EJB 1.0, container-managed fields are limited to
primitives and java.io.Serializable types. Limiting the
container-managed fields to these simple types makes it more difficult
to maintain persistent relationships to other entity beans; entity
beans are always referenced using their java.rmi.Remote interface type,
which is neither a primitive nor Serializable. EJB 1.1 specifies that
container managed fields can include references to other entity beans,
which makes it much easier for the bean developer to model associations
and aggregations of beans. How the container persists the relationships
is not specified, but it's likely that options for converting the
reference to a primary key will be provided at deployment time.

Session Beans

Session beans didn't change much from EJB 1.0 to EJB 1.1 because their
behavior and functionality was well defined in the original
specification. Several clarifications have been added to eliminate
inconsistencies between vendors and provide a more predictable bean
life cycle for developers. For example, the 1.1 specification requires
that the SessionContext be preserved through passivation in
non-transitive fields, and clarifies what resources can be accessed in
which methods. These clarifications tighten up the specification and
make it easier for both vendors and developers.

XML Deployment Descriptors

In EJB 1.0, the deployment descriptor information is stored in a
serializable class defined in the javax.ejb.deployment package. While
at first this appeared to be an excellent approach, it has limitations.
It isn't clear which deployment information should be set by the bean
developer and which by the deployer. In addition, the serializable
classes are not very extensible and limit your ability to describe the
bean's attributes.

To solve these problems, EJB 1.1 replaces the serializable deployment
descriptor with an XML-based deployment descriptor. The XML deployment
descriptor is defined by an XML DTD (document type definition) that
describes the structure that the XML deployment descriptors must have
to be considered valid. The XML format is superior to the previous
serializable object approach in many ways. First, it's more
informative, allowing description tags to be associated with major
elements so that the author can describe, in plain language, the
purpose and expected behavior of attributes. Second, XML deployment
descriptors can be viewed and edited using any text editor; the
serializable deployment descriptor used in EJB 1.0 must be manipulated
in a Java program (unless you're fluent in byte code!). Third, XML is
more extensible and less fragile. In EJB 1.0 there are still problems
with inconsistent versions of the deployment descriptor. As the
specification evolves, the serializable deployment descriptor package
would need to change, which introduces compatibility problems. With
XML, new tags can be added without impacting existing deployment
descriptor parsers. This makes deployment descriptors in EJB 1.1 more
robust. Finally, XML is an industry standard, which means that
deployment descriptors will enjoy universal support.

EJB 1.1 deployment descriptors also add some new declarative attributes
and eliminate some old ones (like Isolation levels!). Security,
transactions, and environment properties change in EJB 1.1,
necessitating changes in the type of information stored in the
deployment descriptor. New features like bean and resource factory
references were added, and the scope of the deployment descriptor was
broadened to cover several beans for assembly. The division of labor
for creating the deployment descriptor is better defined in EJB 1.1 as
well. EJB 1.1 added new roles defining the development, assembly,
deployment, and administration of beans. Contributions made by these
roles to the deployment descriptor are well defined in EJB 1.1.

The Default JNDI context

Environment variables have undergone a fairly radical change.
Environment properties in EJB 1.0 are a powerful mechanism for
modifying the behavior of a component without changing the component's
code. In EJB 1.0, the environment properties are accessible to the bean
through the EJBContext.getEnvironment() method, are set by the bean
developer, and can be modified by the deployer. In EJB 1.0 the
environment variables might, for example, be used to set a maximum
withdraw amount for an account bean, as in the following code:

// EJB 1.0
public class AccountBean implements javax.ejb.EntityBean {

public EntityContext context;

public void withdraw(double amount) throws MaximumLimitException {
try {
Properties environment = context.getEnvironment();
String value = environment.getProperty("withdraw_limit");
Double limit = new Double(value);
if ( amount > limit.doubleValue()){
throw new MaximumLimitException();
else {
// continue processing
}
}
}
}

EJB 1.1 changes the environment properties from the
java.util.Properties class, used in EJB 1.0, to a set of JNDI entries
that exist in a name space called the environment naming context. All
deployed beans in EJB 1.1 have an environment naming context (default
JNDI context) that can be accessed using the JNDI API. This default
JNDI context provides a set of immutable JNDI entries specific to each
type of bean. The default JNDI context provides the bean with access to
environment variables which can be of type String or one of several
primitive wrappers including Double, Float, Integer, or Boolean. The
entries are defined in the XML deployment descriptor using special tags
specific to the environment naming context. Here is an example of how
the default JNDI context is used at run time by an EJB 1.1 bean:

// EJB 1.1
public class AccountBean implements javax.ejb.EntityBean {

public EntityContext context;

public void withdraw(double amount) throws MaximumLimitException {
try {
InitialContext initCxt = InitialContext();
Context defaultCxt =
(Context)initCxt.lookup("java:comp/env");
Double limit = (Double)
defaultCxt.lookup("withdraw_limit");
if ( amount > limit.doubleValue() ){
throw new MaximumLimitException();
else {
// continue processing
}
}
}
}

EJB 1.1 compliant servers are not required to support the EJB 1.0
environment properties, which are available through the EJBContext. The
getEnvironment() method has been deprecated and will throw a run time
exception for EJB 1.1 servers that don't support backward
compatibility.

Environment properties are only one of three sets of values that can be
accessed through the default JNDI context. The default JNDI context is
also used as a repository for linking to predefined resources and
beans. When developing a bean, the bean developer can identify the
types of resources and enterprise beans that will be referenced in the
bean and bind them to the default JNDI context. This simplifies the
process of looking up and obtaining bean references within beans, as
well as locating and using resources like a JDBC database connection.
The bean developer defines the types of beans and resources associated
with the default JNDI entries in the XML deployment descriptor. The
following code shows how a bean uses its default JNDI context to look
up a bean and a JDBC DataSource.

// EJB 1.1
public class TellerBean implements javax.ejb.SessionBean {

public void transfer(int sourceID, int targetID, double amount){

InitialContext initCtx = new InitialContext( );

// look up up the Account home
AccountHome acctHome =
(AccountHome)initCtx.lookup("java:comp/env/ejb/AccountHome");

// transfer the money
Account source = acctHome.findByPrimaryKey(
new AccountKey(sourceID));
Account target = acctHome.findByPrimaryKey(
new AccountKey(targetID);
source.withdraw(amount);
target.deposit(amount);

// Look up a the JDBC data source for recording transactions
javax.sql.DataSource dataSource =
(javax.sql.DataSource)initCtx.lookup("java:comp/env/jdbc/log");
java.sql.Connection con = dataSource.getConnection();

// continue processing: insert a log recording the transaction
}
}

EJB 1.1 standardizes on the use of resource factories to access
resources such as a JDBC connection. A resource factory provides access
to resources in a manner that allows the container to manage the use of
the resource. The use of the javax.sql.DataSource is a perfect example.
It provides the bean with a JDBC connection that is managed
transactionally and securely by the EJB container. In addition to JDBC,
resource factories for the Java Messaging Service can also be used to
obtain and managed access to an asynchronous message service.
The default JNDI context provides a powerful mechanism for obtaining
predefined environment properties, bean references, and resource
factories. It standardizes access to enterprise bean's environment
through JNDI and lays the groundwork for future enhancements. As new
facilities and services are made available to beans, they too can be
accessed through the default JNDI context. A good example is the plan
to provide a "connector" service that will allow beans to connect to
legacy or "back-end" systems using standard resource factories.

RMI over IIOP Narrowing

If the EJB server uses native Java RMI, the bean reference can be cast
into its remote interface as shown in the previous example. If,
however, the EJB client needs to be compatible with EJB servers that
use RMI over IIOP, EJB 1.1 requires that the home reference be
explicitly narrowed before it's returned from the JNDI context.
Explicitly narrowing a remote reference is required because CORBA
doesn't support casting. To support RMI over IIOP we would change the
line that obtains a reference to the AccountHome so that it looks as
follows:

// EJB 1.1 using RMI over IIOP
InitialContext initCtx = new InitialContext();
Object obj = initCtx.lookup("java:comp/env/ejb/AccountHome");
AccountHome acctHome = (AccountHome)
javax.rmi.PortableRemoteObject.narrow(obj, AccountHome.class);

The javax.rmi.PortableRemoteObject class is part of the RMI-IIOP
extension package.

Security

EJB specifies declarative attributes for security authorization. Once a
user has been authenticated (logged in), access to enterprise beans can
be monitored and controlled. The declarative authorization attributes
allows the container to control which users can access which methods on
specific bean types. In EJB 1.0 individual methods on a bean are
associated with Identity objects that represent individual users or
groups of users called roles. Only users that are associated with the
correct Identity objects can access the bean's methods. Using this
approach, bean methods can be mapped to a set of identities in the
serializable deployment descriptor.

When a bean method is invoked at run time, the container examines the
Identity of the caller and compares it to the list of Identity objects
associated with that method. If the caller's identity matches or is a
member of one of the identities associated with the method, the method
can be invoked. Although this authentication model works well-it allows
fine-grained functional authentication without requiring any code in
the bean itself-it also has some problems. In an operational
environment that supports ACL-based security, all the identities and
roles in an enterprise are part of the operational environment. To
choose Identity objects to associate with bean methods, you must have
access to the ACL repository of the environment that the bean will be
deployed in. For this reason, it is normally assumed that the deployer
in EJB 1.0 will map the bean's methods to the security domain.
Unfortunately, deployer may not have a good understanding of the
purpose and function of the bean methods. This makes it difficult for
the deployer to determine what identities and roles should be mapped to
which methods-a problem that is compounded in beans that have numerous
methods.

In recognition of the authentication problems in EJB 1.0, EJB 1.1 has
changed authentication security significantly. In EJB 1.1, the
authentication service remains implicit and fine grained, but it's role
driven rather than method driven. In EJB 1.1 the bean provider and the
application assembler (the same person in many cases) defines logical
roles in the XML deployment descriptor. Each role has a list of methods
it is authorized to access. The roles are logical and have only
semantic meaning. They are not obtained from a specific operational
environment; they simply describe the type of user who can access a set
of methods. The bean developer can provide additional information about
the logical roles by attaching description tags to roles with comments.
The bean deployer, who works in the operational environment, maps the
logical roles defined by the bean developer and application assembler
to real roles in the environment's security system. In this way, the
bean developer and application assembler can define the type of roles
that have access to methods without having to know anything about the
operational environment in which the bean will be deployed. Similarly,
the bean deployer can map roles in the operational environment to the
logical roles in the deployment descriptor, based on semantic meaning
of the logical roles and their attached descriptions, without having to
understand the purpose or function of the methods involved. In fact,
the deployer need not be concerned with the methods at all since she is
only concerned with roles and the methods with which they are
associated.

Looking Ahead to EJB 2.0

Enterprise JavaBeans 2.0 is likely to be the next release of the EJB
specification, which we can expect to be finalized some time after the
year 2000. It will include the definition of a connector interface that
will allow legacy and ERP systems to be accessed by beans through
resource factories. The Java Messaging Service will be integrated into
the EJB architecture, which will probably require the creation of a new
bean type or modification of the stateless session type. The definition
of a standard Object-to-Relational mapping interface is possible,
although this may not be included because of the difficulty in defining
a standard. Finally, a container interface will be defined that will
allow containers and EJB servers to be clearly delineated and therefor
interchangeable.

Anonymous    Aug 01, 1999
Printed
Page 312

line 7 was missing parenthesis. It read
"new AccountKey(targetID);"

It now reads:

"new AccountKey(targetID));

Anonymous    Dec 01, 1999
Printed
Page 315-325

The page numbers in the index have changed due to the addition of Appendix D.

Anonymous    Aug 01, 1999