Configuring Applications

This chapter shows how to configure a bean-client application project so that it can use resources like databases and JavaMail connections, explains how to ensure that each enterprise bean is bound to the appropriate resources, and shows how you can improve the performance of applications through the use of the WOEJBTransport property.

You need to configure three major items before deploying a bean-client application:

You configure bean-client applications by editing the files in Table 6-1.

Table 6-1  The configuration files of a bean-client application

Filename

Purpose

TransactionManagerConfiguration.xml

Defines data sources and JavaMail connections.

GlobalTransactionConfiguration.xml

Defines the JNDI name of a remote data store.

CMPConfiguration.xml

Defines bean-to-table and field-to-column mapping.

OpenEJBConfiguration.xml

Defines enterprise-bean deployment behavior for the container.

The chapter is divided in the following sections:

Configuration Overview

This section gives you a quick look at the configuration process for bean-client applications. It lists the major points you need to look at before deploying your application. It’s divided in the following sections:

Configuring the Transaction Manager

The TransactionManagerConfiguration.xml file is where you enter connection information, such as user name and password, for the data stores that your application’s CMP beans use. You also configure JavaMail. The OpenEJBConfiguration.xml file determines what you need to configure in this file: the data stores, or JavaMail.

If your enterprise beans use container-managed persistence (the OpenEJBConfiguration.xml file contains the string “<res-type>javax.sql.DataSource</res-type>”), you need to configure at least one data source.

Listing 6-1  Example dataSource element of the TransactionManagerConfiguration.xml file

<dataSource>
    <name>DefaultDatabase</name>
    <class>tyrex.resource.jdbc.xa.EnabledDataSource</class>
    <jar>file:/Users/Shared/JDBCDrivers/oracle/oracle8.1.7.1.zip</jar>
    <config>
        <driverName>jdbc:oracle:thin:@xsrv3.apple.com:1521:sqa</driverName>
        <driverClassName>oracle.jdbc.OracleDriver</driverClassName>
        <user>ejb</user>
        <password>ejb</password>
    </config>
    <limits>
        <maximum>100</maximum>
        <minimum>10</minimum>
        <initial>10</initial>
        <maxRetain>300</maxRetain>
        <timeout>50</timeout>
    </limits>
</dataSource>

If your enterprise beans do not use container-managed persistence, you need to delete the resources element from the TransactionManagerConfiguration.xml file, which includes everything between the <resources> tag and the </resources> tag as well as the tags themselves.

If you need to access more than one data store, you can add dataSource elements for each additional data store. See Transaction Manager Configuration for more information.

If your enterprise beans make use of JavaMail (the OpenEJBConfiguration.xml file contains the string “<resource-type>javax.mail.Session</resource-type>”), you need to configure JavaMail.

To configure JavaMail in the TransactionManagerConfiguration.xml file, add this to the data-source section and customize as necessary:

<javamail>
    <name>DefaultSMTPServer</name>
    <property>
        <key>mail.smtp.host</key>
        <value>post.office.com</value>
    </property>
</javamail>

Configuring the EJB Container

Once you have defined the resources that your enterprise beans utilize, you have to review the container environment that WebObjects defined for you in the OpenEJBConfiguration.xml file:

  • Data sources and JavaMail-connection factories.

    If your enterprise beans use more than one data source or rely on JavaMail (as defined in the TransactionManagerConfiguration.xml file, you have to make sure that each bean is linked to the appropriate data source or JavaMail connection factory (through the res-id element inside resource-ref) in the OpenEJBConfiguration.xml file. See resource-ref element.

  • Environment entries.

    Scan the file for env-entry elements and make sure that they contain the appropriate values for your situation. See env-entry element.

  • Method-transaction settings.

    Make sure that the trans-attribute element of method-transaction elements is set to the appropriate transaction type. If the enterprise bean does not define the transaction type for a method, WebObjects sets it to Required. See query element, and method-transaction element for details.

  • One entity-container element per database.

    When your CMP beans use more than one database, you need to

    • group the CMP beans that use the same data store under the same entity-container element

    • create a global transaction manager configuration file by duplicating the GlobalTransactionConfiguration.xml file and changing "Global_TX_Database" so that it names the additional data source (for example, "Global_TX_Personnel")

    In general, you should use the following grouping:

    • one entity-container element per distinct database that encloses its corresponding CMP beans (for more information, see entity-container element)

    • one stateless-session-container element that encloses all stateless beans

    • one stateful-session-container element that encloses all stateful beans

Configuring the Persistence Manager

This section explains how to configure the container for CMP beans. If your application doesn’t use CMP beans, you don’t need to configure the files mentioned here. In fact, the files are only present in your project when at least one of the enterprise-bean frameworks in the project uses container-managed persistence.

GlobalTransactionConfiguration.xml

This is where you define the JNDI name of a remote data store. It must be identical to the name used in the resource-ref element of a bean in the CMPConfiguration.xml file. You must have one global-transaction configuration file per data store.

CMPConfiguration.xml

This is where you map enterprise beans to tables and their fields (or instance variables) to columns in those tables. You also define a bean’s identity or primary key and configure key-value generators. This is an example of a CMPConfiguration.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
                         "http://castor.exolab.org/mapping.dtd">
<mapping>
    <class key-generator="MAX" identity="mPropID" name="webobjectsexamples.realestate.property.PropertyCMPBean">
        <map-to table="EJB_PROPERTY"/>
        <field direct="true" name="mPropID" type="java.lang.Integer">
            <sql name="PROP_ID" type="integer"/>
        </field>
        <field direct="true" name="mPropAddress" type="java.lang.String">
            <sql name="PROP_ADDR" type="varchar"/>
        </field>
        <field direct="true" name="mPropDate" type="java.util.Date">
            <sql name="PROP_LIST_DATE" type="date"/>
        </field>
        <field direct="true" name="mPropPrice" type="float">
            <sql name="PROP_ASK_PRICE" type="real"/>
        </field>
    </class>
</mapping>

For more information, see Persistence Manager Configuration .

Transaction Manager Configuration

WebObjects includes the Tyrex transaction manager. You configure it through the TransactionManagerConfiguration.xml file.

The only item you need to configure for the transaction manager is the domain. A transaction domain provides centralized management of transactions. It defines the policy for all transactions created from that domain, such as default timeout, maximum number of open transactions, support, and journaling. In addition, the domain maintains resource managers such as JDBC data sources and JCA (J2EE Connector Architecture) connectors.

This is an example of a TransactionManagerConfiguration.xml file:

<domain>
  <name>default</name>
   <resources>
     <dataSource>
        <name>DefaultDatabase</name>
        <class>tyrex.resource.jdbc.xa.EnabledDataSource</class>
        <jar>/Library/Java/Extensions/OpenBaseJDBC.jar</jar>
        <config>
            <driverName>jdbc:openbase://localhost/Person</driverName>
            <driverClassName>com.openbase.jdbc.ObDriver</driverClassName>
            <transactionTimeout>60</transactionTimeout>
            <isolationLevel>Serializable</isolationLevel>
        </config>
        <limits>
            <maximum>100</maximum>
            <minimum>10</minimum>
            <initial>10</initial>
            <maxRetain>300</maxRetain>
            <timeout>50</timeout>
        </limits>
    </dataSource>
  </resources>
</domain>

For details on how to write the transaction manager configuration file, see Elements of the Transaction Manager Configuration File.

Persistence Manager Configuration

Container-managed persistence is handled by the Castor JDO component. It generates SQL statements that the container uses to update information in a database. All you have to do is map a database’s table columns to entity beans’ fields.

The persistence manager configuration files specify how the persistence manager obtains a connection to a data store, the mapping between Java classes and the corresponding data-store elements, and the service provider to use to talk to the data store.

These are supported database servers:

You configure the persistence manager by editing three files:

Mapping Enterprise Beans to Database Tables

One of the tasks you need to perform to accomplish bean persistence is to map the enterprise bean fields to be persisted to table columns or other types of permanent storage. You accomplish this by editing the CMPConfiguration.xml file.

The Mapping File

The mapping information you enter in the CMPConfiguration.xml file is written from the point of view of the enterprise bean and describes how the contents of the bean’s fields are translated to and from permanent storage.

This is an example of the contents of the CMPConfiguration.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
                         "http://castor.exolab.org/mapping.dtd">
<mapping>
    <class key-generator="MAX" identity="mPropID" name="webobjectsexamples.realestate.property.PropertyCMPBean">
        <map-to table="EJB_PROPERTY"/>
        <field direct="true" name="mPropID" type="java.lang.Integer">
            <sql name="PROP_ID" type="integer"/>
        </field>
        <field direct="true" name="mPropAddress" type="java.lang.String">
            <sql name="PROP_ADDR" type="varchar"/>
        </field>
        <field direct="true" name="mPropDate" type="java.util.Date">
            <sql name="PROP_LIST_DATE" type="date"/>
        </field>
        <field direct="true" name="mPropPrice" type="float">
            <sql name="PROP_ASK_PRICE" type="real"/>
        </field>
    </class>
</mapping>

For details in how to write the CMPConfiguration.xml file, see Elements of the Component-Managed Persistence Configuration File.

Primary Keys

The persistence manager can generate the values of identity properties automatically with the key generator. When the enterprise bean’s create method is invoked, the persistence manager sets the value of the identity property to the value obtained from the key generator. The key generator can use one of several algorithms available to generate the value. You can use generic algorithms or algorithms specific to a data store. For details on setting the algorithm to use for an enterprise bean’s identity property, see class element and key-generator element.

You can use the key generator only under the following conditions:

  • The primary-key value is not determined from the arguments to the bean’s ejbCreate method.

  • The bean’s identity can be determined through a single field of numeric (byte through long) or String type.

The following sections describe the key-generator algorithms you can use.

MAX

This generic algorithm fetches the maximum value of the primary key (MAX) and locks the record found until the end of the transaction. When the transaction ends, the value generated is MAX + 1. Because of the lock, concurrent transactions that use the same algorithm wait until the end of the original transaction to obtain a new primary-key value. Note that it is still possible to perform multiple inserts during the same transaction.

With this algorithm, duplicate-key exceptions are almost completely avoided. The only case in which they might occur is when inserting a row into an empty table because there are no rows to lock. In this case, the value generated is 1.

This is an example definition of a key generator using the MAX algorithm:

<key-generator name="MAX">
    <param name="table" value="PERSON"/>
    <param name="key-column" value="PERSON_ID"/>
</key-generator>
HIGH/LOW

This generic algorithm needs an auxiliary table or sequence table containing a unique column (the key column) that stores table names and a numeric (integer, bigint, or numeric) column used to reserve primary-key values.

The following table describes the parameters used by the HIGH/LOW key generator.

Table 6-2  HIGH/LOW key generator parameters

Parameter

Description

Use

table

Sequence-table name.

Mandatory

key-column

Name of the column containing table names.

Mandatory

value-column

Name of the column used to reserve primary-key values.

Mandatory

grab-size

Number of primary-key values the key generator reserves at a time.

Optional; default="10"

same-connection

Indicates whether the key generator must use the same connection when accessing the sequence table. Values: (true or false). Must be set to true when working in an EJB environment.

Optional; default="false"

global

Indicates whether the key generator produces globally unique keys. Values: (true or false).

Optional; default="false"

The first time the key generator is called, it finds the row for the target table in the sequence table, locks it, reads the last reserved primary-key value, increases it by the grab size (the number of primary-key values to reserve at a time), and unlocks the row. In subsequent requests for primary-key values for the same target table, the key generator provides primary-key values from the reserved values until it runs out. When it has no more primary-key values, it accesses the sequence table to obtain a new group of primary-key values.

If grab-size is set to 1, the sequence tables contain the true maximum primary-key value at all times. In this case, the HIGH/LOW key generator is essentially equivalent to the MAX key generator.

If global is set to true, the sequence table contains only one row instead of one row per table. The key generator uses this row for all tables.

UUID

This algorithm generates global unique primary-key values. The value generated is a combination of the host’s IP address, the current time in milliseconds since 1970, and a static counter. The complete key consists of a 30-character, fixed-length string. This algorithm has no parameters. The primary-key column must be of type char, varchar, or longvarchar.

IDENTITY

The IDENTITY key generator can be used only with auto-increment primary-key columns (identities) in Sybase ASE/ASA, MS SQL Server, MySQL, and Hypersonic SQL.

After an insert, except when using MySQL or Hypersonic SQL, the key generator obtains the primary-key value from the @@identity system variable, which contains the last identity value for the current database connection. When using MySQL, the system function LAST_INSERT_ID() is used. For Hypersonic SQL, IDENTITY() is used.

SEQUENCE

This algorithm can be used with only Oracle, Oracle8i, PostgreSQL, Interbase, and SAP DB. It generates keys using sequences.

The following table describes the parameters for the SEQUENCE key generator.

Table 6-3  SEQUENCE key generator parameters

Parameter

Description

Use

sequence

Sequence name.

Optional; default="{0}_seq"

returning

RETURNING mode for Oracle8i. Values: (true or false)

Optional; default="false"

increment

Increment for Interbase.

Optional; default="1"

trigger

Indicates whether there is a trigger that generates primary-key values.

Optional; default="false"

Usually a sequence is used for only one table. Therefore, in general, you have to define one key generator per table. However, if you adhere to a naming convention for sequences, you can use one key generator for multiple tables.

For example, if you always obtain sequence names by adding _seq to the name of the corresponding table, you can set sequence to "{0}_seq" (the default).

The way this key generator performs its function depends on the data store being used.

With PostgreSQL, this key generator performs SELECT nextval(sequence_name) before the insert and produces the identity value that is then used when it performs INSERT.

With Interbase, the key generator performs SELECT gen_id(sequence_name, increment) from rdb$database before the insert.

With Oracle, with returning set to "false" by default, and with SAP DB, the key generator transforms the insert statement generated by the persistence manager to the form INSERT INTO table_name (pk_name, ...) VALUES (sequence_name.nextval, ...), executes it, and then it performs SELECT sequene_name.currval FROM table_name to obtain the identity value.

With Oracle8i, when you set returning to "true", RETURNING primary_key_name INTO ? is appended to the insert statement shown above, which is a more efficient procedure to generate primary-key values. Therefore, the persistence manager fetches the identity value when it executes the insert statement (both the insertion and the procurement of the identity value occur in one statement).

If your table has an on_Insert trigger, like the one listed below, that already generates values for the table’s primary key, you can set trigger to "true".

create or replace trigger "trigger_name"
before insert on "table_name" for each row
begin
    select "sequence_name".nextval into :new."pk_name" from dual;
end;

This prevents "sequence_name".nextval from being pulled twice: first during the insert and then in the trigger. It’s also useful in combination with returning="true" for Oracle, in which case you may not specify the sequence name.

Defining Data Sources

Global data-source configuration files tell the transaction manager how to locate a data store using JNDI. They contain the mapping between enterprise beans and tables in a database. The transaction manager then uses the information in TransactionManagerConfiguration.xml to create database connections.

The persistence manager can obtain a connection to a data store in one of three ways:

  • using a JDBC 2.0 driver and URL

  • using a JDBC 2.0 data source

  • using a JNDI data source

If you are deploying the application inside a J2EE environment, you should use the JNDI method because it allows the application server to manage connection pooling and distributed transactions.

To allow for concurrent transactions and to ensure data integrity, two data-store definitions should never use overlapping mappings.

The following is the JNDI configuration of a global data store:

<database name="ebiz" engine="oracle">
    <jndi name="java:comp/env/jdbc/mydb"/>
    <mapping href="Contents/Resources/CMPConfiguration.xml"/>
</database>

Container Configuration

The OpenEJBConfiguration.xml file contains deployment information, as well as transaction and security details. Its contents are divided in two sections: containers, and facilities. WebObjects writes this file for you. However, you need to make additions, especially regarding the transaction type for methods and mapping physical roles to logical roles.

Containers Section

This section of the EJB configuration file holds four types of elements: containers, security-role, method-permission, and method-transaction.

The containers element can contain three types of elements: stateless-session-container, stateful-session-container, and entity-container. Each of these elements holds definitions for the corresponding types of enterprise beans: stateless session bean, stateful session bean, and entity bean (CMP and BMP).

One or more logical security roles are defined using security-role elements. Physical security roles are mapped to logical security roles in the facilities section of the file. You have to define the logical security roles that you want to use in an application. Then, you assign those roles to the methods of the enterprise beans—using method-permission elements—as you see fit.

The method-transaction element tells the container how to manage transactions for each method invocation. You must determine what kind of transaction attribute each enterprise bean’s methods should have, and modify the contents of the method-transaction element as appropriate. Table 6-4 provides a brief explanation of transaction attributes.

Table 6-4  Transaction attributes

Transaction attribute

Meaning

NotSupported

The current transaction is suspended until the method ends.

Supports

If in a transaction, the method is included in it.

Required

The method must be invoked within a transaction. Otherwise, a new transaction is created for the method.

RequiresNew

A new transaction is always created for the method.

Mandatory

The method must be invoked within a transaction. Otherwise, a javax.transaction.TransactionRequiredException is thrown.

Never

The method must never be invoked within a transaction. Otherwise, a java.rmi.RemoteException is thrown.

Listing 6-2 shows an example of the containers section of the EJB configuration file:

Listing 6-2  Example containers section of the OpenEJBConfiguration.xml file

<container-system>
    <containers>
        <stateless-session-container>
            <container-name>Basic Stateless Container</container-name>
            <stateless-bean>
                <description>deployment descriptor for HelloBean</description>
                <display-name>HelloBean</display-name>
                <ejb-deployment-id>HelloBean</ejb-deployment-id>
                <home>com.my.ejb.HelloHome</home>
                <remote>com.my.ejb.Hello</remote>
                <ejb-class>com.my.ejb.HelloBean</ejb-class>
                <transaction-type>Container</transaction-type>
            </stateless-bean>
        </stateless-session-container>
    </containers>
    <security-role>
        <role-name>everyone</role-name>
    </security-role>
    <method-permission>
        <role-name>everyone</role-name>
        <method>
            <ejb-deployment-id>HelloBean</ejb-deployment-id>
            <method-name>*</method-name>
        </method>
    </method-permission>
    <method-transaction>
        <method>
            <ejb-deployment-id>HelloBean</ejb-deployment-id>
            <method-intf>Remote</method-intf>
            <method-name>message</method-name>
            <method-params/>
        </method>
        <trans-attribute>NotSupported</trans-attribute>
    </method-transaction>
</container-system>

Facilities Section

This section of the EJB configuration file specifies the runtime environment: proxy-generation attributes, remote JNDI contexts, database connections, and J2EE services. The elements used are intra-vm-server, remote-jndi-contexts, connectors, and services, respectively. You should not edit this part of the OpenEJBConfiguration.xml file.

Using External Containers

You may want to use an external EJB container instead of an internal one in your bean-client applications when you already have a powerful, reliable container. In this case, you need to remove all the configuration files listed at the beginning of this chapter from your project.

To configure your application to use a single, external EJB container, you need to set system properties when you launch your application. You can set them through the command line. The following list shows the properties you need to set for various EJB containers:

If you want to use more than one EJB container in an application, you have to set these properties through application code. For example, to set the JNDI context for the Web Logic EJB container, you would add the following method listed in Listing 6-3.

Listing 6-3  The initialContext method setting external-container properties

/**
 * Obtains the JNDI context.
 * @return the JNDI context.
 */
public static Context initialContext() throws NamingException {
    Properties properties = new Properties();
 
    properties.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
 
    properties.put(Context.PROVIDER_URL, "t3://<HOST>:<NAMESERVICE_PORT>");
 
    return new InitialContext(properties);
}

Communication Transport Between Bean Clients and Containers

Bean-client applications can communicate with bean containers using one of two transports: Common Object Request Broker Architecture (CORBA) or intra virtual machine. You determine how clients communicate with bean containers through the WOEJBTransport property. It can have one of two values: OpenORB for the CORBA transport or IntraVM for the intra-VM transport.

Using CORBA requires that bean frameworks be built with EJB stubs. See Generating EJB Stubs for information on generating EJB stubs when building bean frameworks. You must use CORBA when the client application and the bean container do not run on the same virtual machine.

When you know that the bean client and the container run on the same virtual machine, you should set the WOEJBTransport property to IntraVM. This streamlines communication between bean client and container as well as facilitate debugging your enterprise beans in Project Builder.

Generating the EJB Configuration Files

After you add bean frameworks to an existing bean-client project, you need to regenerate the EJB configuration files. Also, WebObjects 5.2, bean-client projects do not require the LocalTransactionConfiguration.xml file. Therefore, you need to delete the file from the projects and regenerate the remaining configuration files.

To regenerate the configuration files of a bean-client project, run OpenEJBTool with the bean-client project path and the path of each of the bean frameworks it uses, as shown below.

% cd /System/Library/WebObjects/JavaApplications/OpenEJBTool.woa
 
% ./OpenEJBTool -o <bean-client_project_path> <bean-framework0_path> ... <bean-frameworkN_path>

EJB Container Operation Logging

EJB-container operations are logged using Log4J, which is an open-source package that allows you to turn on logging for an application without changing its source code. Logging is configured through the logging.conf file, which is placed in the Resources group of a project. Listing 6-4 shows the logging.conf file. You modify this file to change the debugging level for the container. For information and documentation on Log4J, see http://jakarta.apache.org/log4j.

Listing 6-4  Logging.conf file

# This file sets up log4j logging for the EJB container
#
# The default setup will log error messages to stdout
 
log4j.rootCategory=warn, R// 1
 
# Fileappender
log4j.appender.F=org.apache.log4j.FileAppender// 2
 
# Edit this line to suit you application name
log4j.appender.F.file=/tmp/application.log// 3
log4j.appender.F.layout=org.apache.log4j.PatternLayout// 4
log4j.appender.F.layout.ConversionPattern=%5p [%t] (%C:%L) - %m%n// 5
 
 
# Console Appender
log4j.appender.R=org.apache.log4j.ConsoleAppender// 6
log4j.appender.R.layout=org.apache.log4j.PatternLayout// 7
log4j.appender.R.layout.ConversionPattern=%5p [%t] (%C:%L) - %m%n// 8
log4j.appender.R.Target=System.err// 9
 
# General logging for the EJB container
#log4j.category.OpenEJB=debug// 10
 
### logging for Container-Managed Persistence
#log4j.category.CastorCMP=debug// 11
 
### CORBA layer logging
#log4j.category.CORBA-Adapter=warn// 12
 
### logging of transaction handling
#log4j.category.Transactions=info// 13
 
### Transaction Manager and Connection Pool logging
#log4j.category.tyrex.default=debug// 14
#log4j.category.tyrex.ots=debug// 15
#log4j.category.tyrex.security=debug// 16
#log4j.category.tyrex.resource=debug// 17
#log4j.category.tyrex.resource.castor=debug// 18
#log4j.category.tyrex.resource.DefaultDatabase=debug// 19

The line numbered 1 configures the logging level of the root category and the output channel. In this case, warn tells Log4J that it should log warnings only. This setting applies to all the subcategories of rootCategory that do not override it. The second argument indicates which appender to use: R for the console output and F for file output.

The lines numbered 2 through 5 configure file logging. You must change only lines 3 through 5, however.

The lines numbered 6 through 9 configure console logging.

The lines numbered 10 through 19 configure the logging level of several components.