Bean Programmer's Guide

Target audience and contents

The target audience for this guide is the Enterprise Bean provider, i.e. the person in charge of developing the software components on the server side (the Enterprise Beans).

The contents of this guide are the following:

  1. Target Audience and content
  2. Introduction
  3. Developing Session Beans
  4. Developing Entity Beans
  5. Transactional Behaviour
  6. Enterprise Bean Environment
  7. Defining the deployment descriptor
  8. Testing the Enterprise Bean
  9. EJB Packaging
  10. Configuring JDBC DataSources
  11. JOnAS specificity

Introduction

An Enterprise Bean is composed of the following parts, that are to be developed by the Enterprise Bean Provider:

The description of these elements in the two cases of enterprise bean (session and entity) is provided in the following sections.

Note: in this documentation, the term "Bean" always means "Enterprise Bean".

Developing Session Beans

A session bean object is a short lived object that executes on behalf of a single client.There are stateless and stateful session beans. Stateless beans don't maintain state across method calls. Any instance of stateless beans can be used by any client at any time. Stateful session beans maintain state within and between transactions. Each stateful session bean object is associated with a specific client. A stateful session bean with container manager transaction demarcation can optionally implement the SessionSynchronization interface. In this case, the bean objects will be informed of transaction boundaries. A rollback could result in a session bean object's state being inconsistent, then implementing the SessionSynchronization interface may allow the bean object to update its state according to the transaction completion status.

The Home Interface

A Session bean's home interface defines one or more create(...) methods. Each create method must be named "create" and must match one of the ejbCreate methods defined in the enterprise Bean class. The return type of a create method must be the enterprise Bean 's remote interface type.

All the exceptions defined in the throws clause of an ejbCreate method must be defined in the throws clause of the matching create method of the home interface.

Example:

In the following examples we will use a Session Bean named Op.

    public interface OpHome extends EJBHome {
	Op create(String user) throws CreateException, RemoteException;
    }
    

The Remote Interface

The Remote Interface is the client's view of an instance of the session bean. This interface contains the business methods of the enterprise bean. The interface must extend the javax.ejb.EJBObject interface. The methods defined in this interface must follow the rules for Java RMI (this means that their arguments and return value must be valid types for java RMI, and their throws clause must include the java.rmi.RemoteException). For each method defined in the remote interface, there must be a matching method in the enterprise Bean's class( same name, same arguments number and types, same return type and same exception list, except for RemoteException).

Example:

    public interface Op extends EJBObject {
        public void buy (int Shares)  throws RemoteException;
        public int  read ()           throws RemoteException;
    }
    

The Enterprise Bean class

This class implements the bean's business methods of the remote interface, and the methods of the SessionBean interface which are those dedicated to the EJB environment. The class must be defined as public, and may not be abstract. The Session Bean interface methods that the EJB provider must develop are the following:

ejbActivate and ejbPassivate are not called in the current jonas version, i.e. beans are always active.

A stateful session Bean with container managed persistence can optionally implement the javax.ejb.SessionSynchronization interface. This interface can provide the Bean with transaction synchronization notification.The Session Synchronization interface methods that the EJB provider must develop are the following:

Example:

package sb;

import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.EJBObject;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.SessionSynchronization;
import javax.naming.InitialContext;
import javax.naming.NamingException;

// This is an example of Session Bean, statefull, and synchronized.

public class OpBean implements SessionBean, SessionSynchronization {

    protected int total = 0;	// actual state of the bean
    protected int newtotal = 0;	// value inside Tx, not yet committed.
    protected String clientUser = null;
    protected SessionContext sessionContext = null;

    public void  ejbCreate(String user) {
	total = 0;
	clientUser = user;
    }

    public void ejbActivate() {
	// Nothing to do for this simple example
    }    

    public void ejbPassivate() {
	// Nothing to do for this simple example
    }

    public void ejbRemove() {
	// Nothing to do for this simple example
    }

    public void setSessionContext(SessionContext sessionContext) {
	this.sessionContext = sessionContext;
    }

    public void afterBegin() {
	newtotal = total;
    }

    public void beforeCompletion() {
	// Nothing to do for this simple example

	// We can access the bean environment everywhere in the bean,
	// for example here!
	try {
	    InitialContext ictx = new InitialContext();
	    String value = (String) ictx.lookup("java:comp/env/prop1");
	    // value should be the one defined in ejb-jar.xml
	} catch (NamingException e) {
	    throw new EJBException(e);
	}
    }

    public void afterCompletion(boolean committed) {
	if (committed) {
	    total = newtotal;
	}
    }

    public void buy(int s) {
	newtotal = newtotal + s;
	return;
    }

    public int read() {
	return newtotal;
    }
}
    

Developing Entity Beans

An entity bean represents persistent data. It is an object view of an entity stored in a relational database. The persistence of an entity bean may be handled in two ways:

Currently, the platform handles persistence in relational storage systems through the JDBC interface. For both container-managed or bean-managed persistence, JDBC connections are obtained from an object provided at the EJB server level, the DataSource. The DataSource interface is defined in the JDBC 2.0 standard extensions. A DataSource object identifies a database and a means to access it via JDBC (a JDBC driver). An EJB server may propose access to several databases and thus provides the corresponding DataSource objects. DataSources are described in more details in the section "Configuring JDBC DataSources".

The Home interface

The Home interface is used by any client application to create and retrieve instances of the entity bean. The bean provider only needs to provide the desired interface, the container will automatically provide the implementation. The interface must extend the javax.ejb.EJBHome interface. The different methods of this interface must follow the rules for java RMI. The signatures of the "create" and "find..." methods should match the signatures of the "ejbCreate" and "ejbFind..." methods that will be provided later in the enterprise bean implementation class (same number and types of arguments, but different return types).

create methods:

finder methods:

Finder methods are used to search for an EJB object or a collection of EJB objects. The arguments of the method are used by the entity bean implementation to locate the requested entity objects. In case of bean-managed persistence, the bean provider is responsible for developing the corresponding ejbFinder methods in the bean implementation. In case of container-managed persistence, the bean provider does not write these methods, they are generated at deployment time by the platform tools; the description of the method is provided in the deployment descriptor, as defined in section "Configuring database access for container-managed persistence". In the Home interface, the finder methods must follow the rules below:

At least one of these methods is mandatory, findByPrimaryKey, which takes as argument a primary key value and returns the corresponding EJB object.

Example

The Account bean example, provided with the platform examples will be used to illustrate these concepts. The state of an entity bean instance is stored into a relational database where the following table should exist :

create table ACCOUNT (ACCNO integer primary key, CUSTOMER varchar(30), BALANCE number(15,4));

public interface AccountHome extends EJBHome {

    public Account create(int accno, String customer, double balance)
        throws RemoteException, CreateException;

    public Account findByPrimaryKey(AccountBeanPK pk)
        throws RemoteException, FinderException;

    public Account findByNumber(int accno)
        throws RemoteException, FinderException;

    public Enumeration findLargeAccounts(double val)
        throws RemoteException, FinderException;
}

The Remote interface

Business methods:

The Remote Interface is the client's view of an instance of the entity bean. It is what is returned to the client by the Home interface after creating or finding an entity bean instance. This interface contains the business methods of the enterprise bean. The interface must extend the javax.ejb.EJBObject interface. The methods of this interface must follow the rules for java RMI. For each method defined in this remote interface, there must be a matching method of the bean implementation class (same arguments number and types, same return type, same exceptions except for RemoteException).

Example

public interface Account extends EJBObject {
    public double getBalance() throws RemoteException;
    public void setBalance(double d) throws RemoteException;
    public String getCustomer() throws RemoteException;
    public void setCustomer(String c) throws RemoteException;
    public int getNumber() throws RemoteException;
}

The Primary Key class

The simple way to define a primary key is to use a standard java class like java.lang.String for example. This must be the type of a field in the bean class. It is not possible to define it as a primitive field (like int, float or boolean for example). The other way is to define its own Primary Key class, as defined here after:

The Primary Key class is necessary for entity beans only. It encapsulates the fields representing the primary key of an entity bean in a single object. The class must be serializable, and must provide suitable implementation of the hashcode() and equals(Object) methods.

For container-managed persistence, the following rules must be followed:

Example

public class AccountBeanPK implements java.io.Serializable {

public int accno;

public AccountBeanPK(int accno) { this.accno = accno; }

public AccountBeanPK() { }

public int hashcode() { return accno; }

public boolean equals(Object other) {

...

}

}

The Enterprise Bean class

The EJB implementation class implements the bean's business methods of the remote interface, and the methods dedicated to the EJB environment, the interface of which are explicitely defined in the EJB specification. The class must implement the javax.ejb.EntityBean interface, must be defined as public, and may not be abstract. The EJB environment dedicated methods that the EJB provider must develop are listed below.

The first set of methods are those corresponding to the create and find methods of the Home interface:

Then, the methods of the javax.ejb.EntityBean interface must be implemented:

Example

This is the example for container-managed persistence. For bean-managed persistence you may refer to the examples delivered with the platform.

package eb;

import java.rmi.RemoteException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.RemoveException;
import javax.ejb.EJBException;

public class AccountImplBean implements EntityBean {

    // Keep the reference on the EntityContext
    protected EntityContext entityContext;

    // Object state
    public int accno;
    public String customer;
    public double balance;

    public AccountBeanPK ejbCreate(int val_accno, String val_customer, double val_balance) {

	// Init object state
	accno = val_accno;
	customer = val_customer;
	balance = val_balance;
	return null;
    }

    public void ejbPostCreate(int val_accno, String val_customer, double val_balance) { 
	// Nothing to be done for this simple example.
    }

    public void ejbActivate() {
	// Nothing to be done for this simple example.
    }

    public void ejbLoad() {
	// Nothing to be done for this simple example, in implicit persistance.
    }

    public void ejbPassivate() {
	// Nothing to be done for this simple example.
    }


    public void ejbRemove() {
	// Nothing to be done for this simple example, in implicit persistance.
    }

    public void ejbStore() {
	// Nothing to be done for this simple example, in implicit persistance.
    }

    public void setEntityContext(EntityContext ctx) {
	// Keep the entity context in object
	entityContext = ctx;
    }

    public void unsetEntityContext() {
	entityContext = null;
    }

    public double getBalance() {
	return balance;
    }

    public void setBalance(double d) {
	balance = balance + d;
    }

    public String  getCustomer() {
	return customer;
    }

    public void setCustomer(String c) {
	customer = c;
    }

    public int getNumber()  {
	return accno;
    }
} 
    

Writing database access operations (bean-managed persistence)

In the case of bean-managed persistence, data access operations are developed by the bean provider using the JDBC interface. However, getting database connections should be done through the javax.sql.DataSource interface provided by the EJB platform. This is mandatory since the EJB platform is responsible for managing the connection pool and for transaction management. So, in order to get a JDBC connection, in each method performing database operations, the bean provider should

A method that performs database access should always contain the getConnection and close statements, as follows:

public void doSomethingInDB (...) {
    conn = dataSource.getConnection();
    ... // Database access operations
    conn.close();
}

A DataSource object associates a JDBC driver and a database (as an ODBC datasource); it is generally registered in JNDI by the EJB server at launch time (see also the section about JDBC DataSources configuration).

A DataSource object is a resource manager connection factory for java.sql. Connection objects implement connections to a database management system. The enterprise bean code refers to resource factories using logical names called "Resource manager connection factory references". The Resource manager connection factory references are special entries in the enterprise bean environment. The bean provider must use resource manager connection factory references to obtain the datasource object as follow:

The deployer binds the resource manager connection factory references to the actual resource factories that are configured in the server. This binding is done in the JOnAS specific deployment descriptor using the jonas-resource element.

Example

The declaration of the resource reference in the standard deployment descriptor looks like:

<resource-ref>
<res-ref-name>jdbc/AccountExplDs</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

The ejbStore method of the same Account example with bean-managed persistence is shown below. It performs JDBC operations to update the database record representing the state of the entity bean instance. The JDBC connection is obtained from the datasource associated to the bean. This datasource has been instanciated by the EJB server and is available for the bean through its resource reference name, which is defined in the standard deployment descriptor.

Somewhere in the bean, a reference to a datasource object of the EJB server is initialized:

it = new InitialContext();

ds = (DataSource)it.lookup("java:comp/env/jdbc/AccountExplDs");

Then, this datasource object is used in the implementation of the methods performing JDBC operations, such as ejbStore, as illustrated below:

public void ejbStore() {
    try { // get a connection
        conn = ds.getConnection();
        // store Object state in DB
        PreparedStatement stmt = conn.prepareStatement("update account set customer=?,balance=? where accno=?");
        stmt.setString(1, customer);
        stmt.setDouble(2, balance);
        AccountBeanPK pk = (AccountBeanPK) entityContext.getPrimaryKey();
        stmt.setInt(3, pk.accno);
        stmt.executeUpdate();
        // close statement
        stmt.close();
        // release connection
        conn.close();
    } catch (SQLException e) {
        throw new javax.ejb.EJBException("Failed to store bean to database", e);
    }
}

Note that the close statement instruction may be important if the server is intensively accessed by many clients performing entity beans access. Since stmt is in the scope of the method, it will be deleted at the end of the method (and the close will be implicitly done), however, it may take some time before the Java garbage collector deletes the statement object, therefore, if the number of clients performing entity bean access is important, the DBMS may raise a "two many opened cursors" exception (a JDBC statement corresponds to a DBMS cursor). Since connection pooling is performed by the platform, closing the connection will not result in a physical connection close, and therefore opened cursors will not be closed. So it is preferable to explicitly close the statement in the method.

It could be a good programming rule to put the JDBC connection and JDBC statement close operations in a finally bloc of the try statement.

Configuring database access for container-managed persistence

First of all, the standard way to indicate to an EJB platform that an entity bean has container-managed persistence is to fill the "ContainerManagedFields" attribute of the deployment descriptor with the list of container-managed fields (the fields that the container will have in charge to make persistent). In the textual format of the deployment descriptor, this is represented by the following line:

ContainerManagedFields = {fieldOne; fieldTwo; fieldFour; };

With container-managed persistence, the programmer does not have to develop the code for accessing the data in the relational database; this code is included in the container itself (generated by the platform tools). However, in order that the EJB platform knows how to access the database and which data to read and write in the database, two kinds of information must be provided with the bean :

The EJB specification does not specify how this information should be provided to the EJB platform by the bean provider and the bean deployer. Therefore, what is described in the remainder of this section is specific to JOnAS.

The bean provider is responsible for defining the mapping of the bean fields to the database table columns. The name of the DataSource may be set at deployment time, as it depends on the EJB platform configuration. This database configuration information is defined in the JOnAS specific deployment descriptor via the jdbc-mapping tag:

    <jdbc-mapping>
      <jndi-name>jdbc_1</jndi-name>
      <jdbc-table-name>accountsample</jdbc-table-name>
      <cmp-field-jdbc-mapping>
      <field-name>accno</field-name>
      <jdbc-field-name>accno</jdbc-field-name>
      </cmp-field-jdbc-mapping>
      <cmp-field-jdbc-mapping>
      <field-name>customer</field-name>
      <jdbc-field-name>customer</jdbc-field-name>
      </cmp-field-jdbc-mapping>
      <cmp-field-jdbc-mapping>
      <field-name>balance</field-name>
      <jdbc-field-name>balance</jdbc-field-name>
    </jdbc-mapping>

jdbc_1 is the JNDI name of the DataSource object identifying the database, Account is the name of the table used to store the bean instances in the database, and mAccno,mCustomer,mBalance are the names of the container-managed fields of the bean to be stored in the accno, customer and balance columns of the Account table. This example applies for container-managed persistence, in case of bean-managed persistence, the database mapping does not exist, and the DataSource name property should be part of the bean properties file.

The jdbc-mapping element may also contain information defining the behaviour of the implementation of a find<method> method (i.e. the ejbFind<method> method, that will be generated by the platform tools). This information is represented by the finder-method-jdbc-mapping element.

For each finder method, this element allows to define a SQL WHERE clause that will be used in the generated finder method implementation to query the relational table storing the bean entities. Note that the table column names should be used, not the bean field names. Example:

      <finder-method-jdbc-mapping>
        <jonas-method>
          <method-name>findLargeAccounts</method-name>
        </jonas-method>
        <jdbc-where-clause>where balance > ?</jdbc-where-clause>
      </finder-method-jdbc-mapping>

    

The previous finder method description will cause the platform tools to generate an implementation of ejbFindLargeAccount(double arg) that returns the primary keys of the entity bean objects corresponding to the tuples returned by the "select ... from Account where balance > ?" where '?' will be replaced by the value of the first argument of the findLargeAccount method. If several '?' characters appear in the provided where clause, this means that the finder method has several arguments, and they will correspond to these arguments, respecting the order of the method signature.

The datatypes supported for container-managed fields are the following:
Java Type  JDBC Type  JDBC driver Access methods 
boolean BIT getBoolean(), setBoolean() 
byte TINYINT getByte(), setByte() 
short  SMALLINT getShort(), setShort() 
int  INTEGER getInt(), setInt() 
long  BIGINT getLong(), setLong() 
float  FLOAT getFloat(), setFloat() 
double  DOUBLE getDouble(), setDouble 
byte[]  VARBINARY or LONGVARBINARY (1) getBytes(), setBytes() 
java.lang.String  VARCHAR or LONGVARCHAR (1)  getString(), setString() 
java.lang.Boolean  BIT getObject(), setObject() 
java.lang.Integer  INTEGER getObject(), setObject() 
java.lang.Short SMALLINT getObject(), setObject()
java.lang.Long  BIGINT getObject(), setObject() 
java.lang.Float  REAL getObject(), setObject() 
java.lang.Double  DOUBLE getObject(), setObject() 
java.math.BigDecimal  NUMERIC getObject(), setObject() 
java.sql.Date  DATE getDate(), setDate() 
java.sql.Time  TIME getTime(), setTime() 
java.sql.Timestamp  TIMESTAMP getTimestamp(), setTimestamp()
any serializable class VARBINARY or LONGVARBINARY (1) getBytes(), setBytes()

(1) The mapping for String will normally be VARCHAR but will turn into LONGVARCHAR if the given value exceeds the driver's limit on VARCHAR values. The case is similar for byte[] and VARBINARY and LONGVARBINARY values.

Transactional Behaviour

Declarative transaction management

In case of bean managed transaction management, the transactional behaviour of an enterprise bean is defined at configuration time, and is part of the assembly-descriptor element of the standard deployment descriptor.. It is possible to define a common behaviour for all the methods of the bean, or to define it at the method level. It is achieved by specifying a transactional attribute, which can be one of the following:

This is illustrated in the following table:
Transaction Attribute  Client transaction  Transaction associated with enterprise Bean's method 
NotSupported 

T1

-

Required 

T1

T2 

T1

RequiresNew 

T1

T2 

T2

Mandatory 

T1

error 

T1

Supports

T1

T1

Never -

T1

-

error

In the deployment descriptor, the specification of the transactional attributes appears in the assembly-descriptor as follows:

  <assembly-descriptor>
    <container-transaction>
      <method>
      <ejb-name>AccountImpl</ejb-name>
      <method-name>*</method-name>
      </method>
      <trans-attribute>Supports</trans-attribute>
    </container-transaction>
    <container-transaction>
      <method>
      <ejb-name>AccountImpl</ejb-name>
      <method-name>getBalance</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
    <container-transaction>
      <method>
      <ejb-name>AccountImpl</ejb-name>
      <method-name>setBalance</method-name>
      </method>
      <trans-attribute>Mandatory</trans-attribute>
    </container-transaction>
  </assembly-descriptor>
    

In this example, for all methods of the AccountExpl bean which are not explicitly specified in a container-transaction element, the default transactional attribute is Supports (defined at the bean-level), and for the methods getBalance and setBalance their respective transactional attributes are Required and andatory (defined at the method-name level).

A session bean that manages its transactions itself must set in its standard deployment descriptor "transaction-type=Bean".

To demarcate the transaction boundaries in a session bean with bean-managed transactions, the bean programmer should use the javax.transaction.UserTransaction interface, defined on an EJB server object that may be obtained using the EJBContext.getUserTransaction() method (the SessionContext object or the EntityContext object depending if the method is defined on a session or an entity bean). The example below shows a session bean method "doTxJob" demarcating the transaction boundaries; the UserTransaction object is obtained from the sessionContext object, that should have been initialized in the setSessionContext method (see the example of the session bean).

public void doTxJob() throws  RemoteException {
     UserTransaction ut = sessionContext.getUserTransaction();
     ut.begin();
     ... // transactional operations
     ut.commit();
}

Another way to do this is to use JNDI and retrieve UserTransaction with the adress "java:comp/UserTransaction" in the initial context.

Distributed transaction management

As explained in the previous section, the transactional behaviour of an application can be defined in a declarative way, or coded in the bean and/or the client itself (transaction boundaries demarcation). In any case, the distribution aspects of the transactions are completely transparent to the bean provider and to the application assembler. This means that a transaction may involve beans located on several EJB servers and that the platform will take in charge the management of the global transaction by itself. It will perform the two phase commit protocol between the different servers and the bean programmer will have nothing to do for this purpose.

Once the beans have been developed and the application has been assembled, it is possible for the deployer and for the administrator to configure the distribution of the different beans on one or several machines, and within one or several EJB servers. This may be done without impacting neither the beans code nor their deployment descriptors. The distributed configuration is specified at launch time: in the environment properties of an EJB server, you may specify

To achieve this goal, two properties must be set in your jonas.properties file, jonas.beans.descriptors and jonas.tm.remote. The first one lists the beans that will be handled on this EJB server (by specifying the name of their deployment descriptors), and the second one sets the Java Transaction Monitor (JTM) launching mode:

Example:

jonas.beans.descriptors       Bean1.xml, Bean2.xml
jonas.tm.remote               false

The Java Transaction Monitor may run outside of any EJB server, in this case, it may be launched in a stand alone way using the following command:

TMServer

Using this configuration facilities, it is possible to adapt the beans distribution to the resources (cpu and data) location, so that performance may be optimal.

The figure below illustrates four cases of distribution configuration of three beans.

Figure illustrating beans distr

  1. Case 1: The three beans B1, B2 and B3 are located on the same EJB server, which embeds a Java Transaction Monitor.
  2. Case 2: The three beans are located on different EJB servers, one of them running the Java Transaction Monitor, which manages the global transaction.
  3. Case 3: The three beans are located on different EJB servers, the Java Transaction Monitor is running outside of any EJB server.
  4. Case 4: The three beans are located on different EJB servers. Each EJB server is running a Java Transaction Monitor. One of the JTM acts as the master monitor, while the two others are slaves.

These different configuration cases may be obtained by launching the EJB servers and eventually the JTM (case 3) with the adequate properties. The rational when opting for one of these configurations is resources location and load balancing. However, the following hints may be noted:

Enterprise Bean's Environment

The enterprise bean environment is a mechanism to allows customization of the enterprise bean's business logic during assembly or deployment. The enterprise bean's environment allows the enterprise bean to be customized without the need to access or change the enterprise bean's source code.

The enterprise bean environment is provided by the container (i.e. the JOnAS server) to the bean through the JNDIinterface as a JNDI context. The bean code access the environment using JNDI with names starting with "java:comp/env/".

The bean provider declares in the deployment descriptor all the bean environment via the env-entry element. The deployer can set or modify the values of the environment entries.

example

A bean accesses its environment entries with a code like this:

    InitialContext ictx = new InitialContext();
    Context myenv = ictx.lookup("java:comp/env");
    Integer min = (Integer) myenv.lookup("minvalue");
    Integer max = (Integer) myenv.lookup("maxvalue");
    

In the standard deployment descriptor, the declaration of these variables are:

    <env-entry>
      <env-entry-name>minvalue</env-entry-name>
      <env-entry-type>java.lang.Integer</env-entry-type>
      <env-entry-value>12</env-entry-value>
    </env-entry>
    <env-entry>
      <env-entry-name>maxvalue</env-entry-name>
      <env-entry-type>java.lang.Integer</env-entry-type>
      <env-entry-value>120</env-entry-value>
    </env-entry>
    

deprecated EJBContext.getEnvironment() method

JOnAS provides support for EJB 1.0 style of definition of environment properties. EJB1.0 environment must be declared in the ejb10-properties sub-context. For example:

    <env-entry>
      <env-entry-name>ejb10-properties/foo</env-entry-name>
      <env-entry-type>java.lang.String</env-entry-type>
      <env-entry-value>foo value</env-entry-value>
    </env-entry>
    

The bean can retrieve its environment with this code:

    SessionContext ctx;
    Properties prop;
    public void setSessionContext(SessionContext sc) {
        ctx = sc;
        prop = ctx.getEnvironment();
    }
    public mymethod() {
        String foo = prop.getProperty("foo");
        ...
    }
    

Defining the deployment descriptor

The bean programmer is also responsible for providing the deployment descriptor associated to the developped Enterprise Beans. As one goes along the different steps of the application development and deployment life cycle of the Enterprise JavaBeans, (development, assembly, deployment), the deployment descriptor has to be completed.

The Bean Provider's responsabilities and the Application Assembler's responsabilities is to provide a XML deployment descriptor respecting the deployment descriptor's XML DTD defined in the EBJ specification version 1.1. (See $JONAS_ROOT/xml/ejb-jar_1_1.dtd).

To be able to deploy Enterprise JavaBeans on the EJB server, information not defined in the standard XML deployment descriptor are needeed, such information about the mapping of the bean to the underlying database for entity bean with container-managed persistence, for example. Theses datas are specified, in the deployment step, in another XML deployment descriptor which is specific to JOnAS. The JOnAS specific deployment descriptor's XML DTD can be found in $JONAS_ROOT/xml/jonas-ejb-jar.dtd. The file's name of the JOnAS specific XML deployment descriptor must be the file's name of the standard XML deployment descriptor prefixed by 'jonas-'.

The standard deployment descriptor should contain the following structural information for each enterprise bean:

The JOnAS specific deployment descriptor contains the following information for each enterprise bean:

Example of Session descriptors:

<!DOCTYPE ejb-jar SYSTEM "......./xml/ejb-jar_1_1.dtd">
<ejb-jar>
  <description>Here is the description of the test's beans</description>
  <enterprise-beans>
    <session>
      <description>... Bean example one ...</description>
      <display-name>Bean example one</display-name>
      <ejb-name>ExampleOne</ejb-name>
      <home>tests.Ex1Home</home>
      <remote>tests.Ex1</remote>
      <ejb-class>tests.Ex1Bean</ejb-class>
      <session-type>Stateful</session-type>
      <transaction-type>Container</transaction-type>
      <env-entry>
	<env-entry-name>name1</env-entry-name>
	<env-entry-type>java.lang.String</env-entry-type>
	<env-entry-value>value1</env-entry-value>
      </env-entry>
      <resource-ref>
	<res-ref-name>jdbc/mydb</res-ref-name>
	<res-type>javax.sql.DataSource</res-type>
	<res-auth>Application</res-auth>
      </resource-ref>
    </session>
  </enterprise-beans>
  <assembly-descriptor>
    <container-transaction>
      <method>
	<ejb-name>ExampleOne</ejb-name>
	<method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
    <container-transaction>
      <method>
	<ejb-name>ExampleOne</ejb-name>
        <method-inter>Home</ejb-name>
	<method-name>*</method-name>
      </method>
      <trans-attribute>Supports</trans-attribute>
    </container-transaction>
    <container-transaction>
      <method>
	<ejb-name>ExampleOne</ejb-name>
	<method-name>methodOne</method-name>
      </method>
      <trans-attribute>NotSupported</trans-attribute>
    </container-transaction>
    <container-transaction>
      <method>
	<ejb-name>ExampleOne</ejb-name>
	<method-name>methodTwo</method-name>
        <method-params><method-param>int</method-param></method-params>
      </method>
      <trans-attribute>Mandatory</trans-attribute>
    </container-transaction>
    <container-transaction>
      <method>
	<ejb-name>ExampleOne</ejb-name>
	<method-name>methodTwo</method-name>
        <method-params><method-param>java.lang.String</method-param></method-params>
      </method>
      <trans-attribute>NotSupported</trans-attribute>
    </container-transaction>
  </assembly-descriptor>
</ejb-jar>


<!DOCTYPE jonas-ejb-jar SYSTEM "......./xml/jonas-ejb-jar.dtd">
<jonas-ejb-jar>
  <jonas-session>
    <ejb-name>ExampleOne</ejb-name>
    <jndi-name>ExampleOneHome</jndi-name>
    <jonas-resource>
      <res-ref-name>jdbc/mydb</res-ref-name>
      <jndi-name>jdbc_1</jndi-name>
    </jonas-resource>
  </jonas-session>
</jonas-ejb-jar>

Example of container-managed persistence Entity Descriptor


<!DOCTYPE ejb-jar SYSTEM "......./xml/ejb-jar_1_1.dtd">
<ejb-jar>
  <description>Here is the description of the test's beans</description>
  <enterprise-beans>
    <entity>
      <description>... Bean example one ...</description>
      <display-name>Bean example two</display-name>
      <ejb-name>ExampleTwo</ejb-name>
      <home>tests.Ex2Home</home>
      <remote>tests.Ex2</remote>
      <ejb-class>tests.Ex2Bean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>tests.Ex2PK</prim-key-class>
      <reentrant>False</reentrant>
      <cmp-field>
	<field-name>field1</field-name>
      </cmp-field>
      <cmp-field>
	<field-name>field2</field-name>
      </cmp-field>
      <cmp-field>
	<field-name>field3</field-name>
      </cmp-field>
      <primkey-field>field3</primkey-field>
      <env-entry>
	<env-entry-name>name1</env-entry-name>
	<env-entry-type>java.lang.String</env-entry-type>
	<env-entry-value>value1</env-entry-value>
      </env-entry>
    </entity>
  </enterprise-beans>
  <assembly-descriptor>
    <container-transaction>
      <method>
	<ejb-name>ExampleTwo</ejb-name>
	<method-name>*</method-name>
      </method>
      <trans-attribute>Supports</trans-attribute>
    </container-transaction>
  </assembly-descriptor>
</ejb-jar>


<!DOCTYPE jonas-ejb-jar SYSTEM "......./xml/jonas-ejb-jar.dtd">
<jonas-ejb-jar>
  <jonas-entity>
    <ejb-name>ExampleTwo</ejb-name>
    <jndi-name>ExampleTwoHome</jndi-name>
    <jdbc-mapping>
      <jndi-name>jdbc_1</jndi-name>
      <jdbc-table-name>YourTable</jdbc-table-name>
      <cmp-field-jdbc-mapping>
	<field-name>field1</field-name>
	<jdbc-field-name>dbf1</jdbc-field-name>
      </cmp-field-jdbc-mapping>
      <cmp-field-jdbc-mapping>
	<field-name>field2</field-name>
	<jdbc-field-name>dbf2</jdbc-field-name>
      </cmp-field-jdbc-mapping>
      <cmp-field-jdbc-mapping>
	<field-name>field3</field-name>
	<jdbc-field-name>dbf3</jdbc-field-name>
      </cmp-field-jdbc-mapping>
      <finder-method-jdbc-mapping>
	<jonas-method>
	  <method-name>findByField1</method-name>
	</jonas-method>
	<jdbc-where-clause>where dbf1 = ?</jdbc-where-clause>
      </finder-method-jdbc-mapping>
    </jdbc-mapping>
  </jonas-entity>
</jonas-ejb-jar>
    

Testing the Enterprise Bean

In order to test the enterprise bean, the bean developer should develop a client application (see chapter Application Assembler's Guide), and follow the following steps:

  1. Write the bean classes and the deployment descriptors (the standard and the JOnAS specific)
  2. Compile the source files: the Home and Remote interfaces, the bean implementation, the bean primary key class (if there is some),
  3. Generate the container (or interposition) classes and compile them, using the GenIC tool.
For the Account entity bean example, if we suppose that the test client application has been developped in the file ClientAccount.java, the compilation and generation steps are the following:
javac Account.java AccountBeanPK.java ClientAccount.java AccountBean.java AccountHome.java
GenIC ejb-jar.xml
    

EJB Packaging

Enterprise Beans are packaged for deployment in a standard Java programming language Archive file, called an ejb-jar file. This file must contain

The beans' class files
the class files of the remote and home interfaces, of the beans' implementations, of the beans' primary key classes (if there are some), and of all necessary classes.
The beans' deployment descriptor
The ejb-jar file must contain the deployment descriptors, made up of:

Example

To build the ejb-jar file of the Account entity bean example, the java source files should have been compiled to obtain the class files and the two XML deployment descriptors should have been written.

Then, the ejb-jar file (OpEB.jar) is built, using the jar command:

    cd your_bean_class_directory
    mkdir META-INF
    cp .../eb/*.xml META-INF
    jar cvf OpEB.jar sb/*.class META-INF/*.xml
    

Configuring JDBC DataSources

For both container-managed or bean-managed persistence, JOnAS makes use of relational storage systems through the JDBC interface. JDBC connections are obtained from an object provided at the EJB server level, the DataSource. The DataSource interface is defined in the JDBC 2.0 standard extensions. A DataSource object identifies a database and a mean to access it via JDBC (a JDBC driver). An EJB server may propose access to several databases and thus provide the corresponding DataSource objects. One may add DataSource objects available on the platform; they are defined in the jonas.properties file. This section explains how DataSource objects may be defined and configured in the EJB server, although this is more an administrative task than a bean developer task.

Implementations of the DataSource interface should be provided by the JDBC driver vendors. As such classes are not currently available, JOnAS provides such an implementation of the DataSource interface that allows you to define DataSource objects for two relational database management server products, Oracle, PostGres and InstantDB (a free, 100% java, Relational Database Management System).

The way to define DataSource objects in order to make them available to an EJB platform is not specified in the EJB specification. Therefore, the remainder of this section, which describes how to define and configure DataSource objects, is specific to JOnAS. However, the way to use these DataSource objects in the Enterprise Bean methods is standard by the way of the resource manager connection factory references (example in section "Writing database access operations").

A DataSource object should be defined in a file called <DataSource name>.properties (for example Oracle1.properties for an Oracle DataSource and InstantDB1.properties for an InstantDB DataSource, as delivered with the platform).

In the jonas.properties file, to define a DataSource "Oracle1", you should add its name "Oracle1" (name of the properties file) to the line jonas.datasources, as follows:

jonas.datasources Oracle1,InstantDB1

The property file defining a DataSource should contain the following information:

datasource.name  JNDI name of the DataSource 
datasource.url  The JDBC database URL : jdbc:<database_vendor_subprotocol>:...
datasource.classname Name of the class implementing the JDBC driver
datasource.username  Database user name 
datasource.password  Database user password 

A DataSource object for Oracle (say Oracle1), named "jdbc_1" in JNDI, and using the Oracle "thin" JDBC driver, should be described in a file called Oracle1.properties, as in the example below:

datasource.name       jdbc_1
datasource.url        jdbc:oracle:thin:@malte:1521:ORA1
datasource.classname  oracle.jdbc.driver.OracleDriver
datasource.username   scott
datasource.password   tiger 

In this example, "malte" is the hostname of the server running the Oracle DBMS, 1521 is the SQL*Net V2 port number on this server, and ORA1 is the ORACLE_SID.

This example makes use of the Oracle "Thin" JDBC driver. If your EJB server is running on the same host as the Oracle DBMS, you may use the Oracle OCI JDBC driver; in this case the URL to be used is jdbc:oracle:oci7: or jdbc:oracle:oci8:, depending of your Oracle release. Oracle JDBC drivers may be downloaded at their Web site.

If you intend to create an InstantDB DataSource object (say InstantDB1), named "jdbc_2" in JNDI, it should be described as follows (in a file InstantDB1.properties):

datasource.name                jdbc_2 
datasource.url                 jdbc:idb=Account.prp
datasource.classname           jdbc.idbDriver
datasource.username            unuseful
datasource.password            unuseful

Properties having the "unuseful" value are not used for this kind of persistence storage.

For the database user and password, you may choose to put it in the DataSource description (<DataSource name>.properties file) and your Enterprise Beans use the getConnection() method, or not to have it in the DataSource description and having your Enterprise Bean use the getConnection(String username, String password) method.

ConnectionManager configuration

Each Datasource is implemented as a connection manager that can be configured via some additional properties described in the following table. Look at the Oracle1.properties file to see an example of settings. All these settings have default values and are not required.

property name description default value
jdbc.connchecklevel JDBC connection checking level 1
jdbc.connmaxage max age for jdbc connections 30
jdbc.connmaxthreads max concurrent threads on same tx/connection 4
jdbc.connexcltimeout max wait time if more than connmaxthreads threads request conn 30
jdbc.connteststmt test statement select 1

JOnAS specificity

This section lists the different aspects in the Enterprise Bean development cycle which are specific to JOnAS (since they are not defined in the EJB specification).

Configuration of DataSource objects

Definition and configuration of the DataSource objects available on the EJB platform. See section "Configuring JDBC DataSources".

JOnAS specific deployment descriptor

JOnAS specific deployment descriptor that contains information assigned by the deployer:

isModified optimization

In order to improve performance we have implemented in JOnAS the isModified extension. Before performing an update the container will call a method of the bean whose name is indicated in the is-modified-method-name element of the JOnAS specific deployment descriptor. This method is responsible to tell if the state of the bean has been changed. Doing so, the container is able to know if it must store data in the database or if it's useless.

example

The bean implementation manages a boolean isDirty and implements a method that returns the value of this boolean: isModified

    
    private transient boolean isDirty;
    public boolean isModified() {
	return isDirty;
    }
    

The JOnAS specific deployment descriptor indicates that the bean implements an isModified method:

 
    <jonas-entity>
      <ejb-name>Item</ejb-name>
      <is-modified-method-name>isModified</is-modified-method-name>
      ...