The Java EE 6 Tutorial

Part VIII Java EE Supporting Technologies

Part VIII explores several technologies that support the Java EE platform. This part contains the following chapters:

Chapter 27 Introduction to Java EE Supporting Technologies

The Java EE platform includes several technologies and APIs that extend its functionality. These technologies allow applications to access a wide range of services in a uniform manner. These technologies are explained in greater detail in Chapter 28, Transactions and Chapter 29, Resource Connections, as well as Chapter 30, Java Message Service Concepts, Chapter 31, Java Message Service Examples, and Chapter 32, Advanced Bean Validation Concepts and Examples.

The following topics are addressed here:

Transactions

In a Java EE application, a transaction is a series of actions that must all complete successfully, or else all the changes in each action are backed out. Transactions end in either a commit or a rollback.

The Java Transaction API (JTA) allows applications to access transactions in a manner that is independent of specific implementations. JTA specifies standard Java interfaces between a transaction manager and the parties involved in a distributed transaction system: the transactional application, the Java EE server, and the manager that controls access to the shared resources affected by the transactions.

The JTA defines the UserTransaction interface that applications use to start, commit, or abort transactions. Application components get a UserTransaction object through a JNDI lookup by using the name java:comp/UserTransaction or by requesting injection of a UserTransaction object. An application server uses a number of JTA-defined interfaces to communicate with a transaction manager; a transaction manager uses JTA-defined interfaces to interact with a resource manager.

See Chapter 28, Transactions for a more detailed explanation. The JTA 1.1 specification is available at http://www.oracle.com/technetwork/java/javaee/tech/jta-138684.html.

Resources

A resource is a program object that provides connections to such systems as database servers and messaging systems.

The Java EE Connector Architecture and Resource Adapters

The Java EE Connector Architecture enables Java EE components to interact with enterprise information systems (EISs) and EISs to interact with Java EE components. EIS software includes such kinds of systems as enterprise resource planning (ERP), mainframe transaction processing, and nonrelational databases. Connector architecture simplifies the integration of diverse EISs. Each EIS requires only one implementation of the Connector architecture. Because it adheres to the Connector specification, an implementation is portable across all compliant Java EE servers.

The specification defines the contracts for an application server as well as for resource adapters, which are system-level software drivers for specific EIS resources. These standard contracts provide pluggability between application servers and EISs. The Java EE Connector Architecture 1.6 specification defines new system contracts such as Generic Work Context and Security Inflow. The Java EE Connector Architecture 1.6 specification is available at http://jcp.org/en/jsr/detail?id=322.

A resource adapter is a Java EE component that implements the Connector architecture for a specific EIS. A resource adapter can choose to support the following levels of transactions:

See Chapter 29, Resource Connections for a more detailed explanation of resource adapters.

Java Database Connectivity Software

To store, organize, and retrieve data, most applications use relational databases. Java EE applications access relational databases through the JDBC API.

A JDBC resource, or data source, provides applications with a means of connecting to a database. Typically, a JDBC resource is created for each database accessed by the applications deployed in a domain. Transactional access to JDBC resources is available from servlets, JavaServer Faces pages, and enterprise beans. The connection pooling and distributed transaction features are intended for use by JDBC drivers to coordinate with an application server. For more information, see DataSource Objects and Connection Pools.

Java Message Service

Messaging is a method of communication between software components or applications. A messaging system is a peer-to-peer facility: A messaging client can send messages to, and receive messages from, any other client. Each client connects to a messaging agent that provides facilities for creating, sending, receiving, and reading messages.

The Java Message Service (JMS) API allows applications to create, send, receive, and read messages. It defines a common set of interfaces and associated semantics that allow programs written in the Java programming language to communicate with other messaging implementations.

The JMS API minimizes the set of concepts a programmer must learn in order to use messaging products but provides enough features to support sophisticated messaging applications. It also strives to maximize the portability of JMS applications across JMS providers in the same messaging domain.

Chapter 28 Transactions

A typical enterprise application accesses and stores information in one or more databases. Because this information is critical for business operations, it must be accurate, current, and reliable. Data integrity would be lost if multiple programs were allowed to update the same information simultaneously or if a system that failed while processing a business transaction were to leave the affected data only partially updated. By preventing both of these scenarios, software transactions ensure data integrity. Transactions control the concurrent access of data by multiple programs. In the event of a system failure, transactions make sure that after recovery, the data will be in a consistent state.

The following topics are addressed here:

What Is a Transaction?

To emulate a business transaction, a program may need to perform several steps. A financial program, for example, might transfer funds from a checking account to a savings account by using the steps listed in the following pseudocode:

begin transaction
	debit checking account
	credit savings account
	update history log
commit transaction

Either all or none of the three steps must complete. Otherwise, data integrity is lost. Because the steps within a transaction are a unified whole, a transaction is often defined as an indivisible unit of work.

A transaction can end in two ways: with a commit or with a rollback. When a transaction commits, the data modifications made by its statements are saved. If a statement within a transaction fails, the transaction rolls back, undoing the effects of all statements in the transaction. In the pseudocode, for example, if a disk drive were to crash during the credit step, the transaction would roll back and undo the data modifications made by the debit statement. Although the transaction fails, data integrity would be intact because the accounts still balance.

In the preceding pseudocode, the begin and commit statements mark the boundaries of the transaction. When designing an enterprise bean, you determine how the boundaries are set by specifying either container-managed or bean-managed transactions.

Container-Managed Transactions

In an enterprise bean with container-managed transaction demarcation, the EJB container sets the boundaries of the transactions. You can use container-managed transactions with any type of enterprise bean: session or message-driven. Container-managed transactions simplify development because the enterprise bean code does not explicitly mark the transaction’s boundaries. The code does not include statements that begin and end the transaction. By default, if no transaction demarcation is specified, enterprise beans use container-managed transaction demarcation.

Typically, the container begins a transaction immediately before an enterprise bean method starts and commits the transaction just before the method exits. Each method can be associated with a single transaction. Nested or multiple transactions are not allowed within a method.

Container-managed transactions do not require all methods to be associated with transactions. When developing a bean, you can set the transaction attributes to specify which of the bean’s methods are associated with transactions.

Enterprise beans that use container-managed transaction demarcation must not use any transaction-management methods that interfere with the container’s transaction demarcation boundaries. Examples of such methods are the commit, setAutoCommit, and rollback methods of java.sql.Connection or the commit and rollback methods of javax.jms.Session. If you require control over the transaction demarcation, you must use application-managed transaction demarcation.

Enterprise beans that use container-managed transaction demarcation also must not use the javax.transaction.UserTransaction interface.

Transaction Attributes

A transaction attribute controls the scope of a transaction. Figure 28–1 illustrates why controlling the scope is important. In the diagram, method-A begins a transaction and then invokes method-B of Bean-2. When method-B executes, does it run within the scope of the transaction started by method-A, or does it execute with a new transaction? The answer depends on the transaction attribute of method-B.

Figure 28–1 Transaction Scope

A diagram showing a transaction between two beans.

A transaction attribute can have one of the following values:

Required Attribute

If the client is running within a transaction and invokes the enterprise bean’s method, the method executes within the client’s transaction. If the client is not associated with a transaction, the container starts a new transaction before running the method.

The Required attribute is the implicit transaction attribute for all enterprise bean methods running with container-managed transaction demarcation. You typically do not set the Required attribute unless you need to override another transaction attribute. Because transaction attributes are declarative, you can easily change them later.

RequiresNew Attribute

    If the client is running within a transaction and invokes the enterprise bean’s method, the container takes the following steps:

  1. Suspends the client’s transaction

  2. Starts a new transaction

  3. Delegates the call to the method

  4. Resumes the client’s transaction after the method completes

If the client is not associated with a transaction, the container starts a new transaction before running the method.

You should use the RequiresNew attribute when you want to ensure that the method always runs within a new transaction.

Mandatory Attribute

If the client is running within a transaction and invokes the enterprise bean’s method, the method executes within the client’s transaction. If the client is not associated with a transaction, the container throws a TransactionRequiredException.

Use the Mandatory attribute if the enterprise bean’s method must use the transaction of the client.

NotSupported Attribute

If the client is running within a transaction and invokes the enterprise bean’s method, the container suspends the client’s transaction before invoking the method. After the method has completed, the container resumes the client’s transaction.

If the client is not associated with a transaction, the container does not start a new transaction before running the method.

Use the NotSupported attribute for methods that don’t need transactions. Because transactions involve overhead, this attribute may improve performance.

Supports Attribute

If the client is running within a transaction and invokes the enterprise bean’s method, the method executes within the client’s transaction. If the client is not associated with a transaction, the container does not start a new transaction before running the method.

Because the transactional behavior of the method may vary, you should use the Supports attribute with caution.

Never Attribute

If the client is running within a transaction and invokes the enterprise bean’s method, the container throws a RemoteException. If the client is not associated with a transaction, the container does not start a new transaction before running the method.

Summary of Transaction Attributes

Table 28–1 summarizes the effects of the transaction attributes. Both the T1 and the T2 transactions are controlled by the container. A T1 transaction is associated with the client that calls a method in the enterprise bean. In most cases, the client is another enterprise bean. A T2 transaction is started by the container just before the method executes.

In the last column of Table 28–1, the word “None” means that the business method does not execute within a transaction controlled by the container. However, the database calls in such a business method might be controlled by the transaction manager of the database management system.

Table 28–1 Transaction Attributes and Scope

Transaction Attribute 

Client’s Transaction 

Business Method’s Transaction 

Required

None 

T1 

T2 

T1 

RequiresNew

None 

T1 

T2 

T2 

Mandatory

None 

T1 

Error 

T1 

NotSupported

None 

T1 

None 

None 

Supports

None 

T1 

None 

T1 

Never

None 

T1 

None 

Error 

Setting Transaction Attributes

Transaction attributes are specified by decorating the enterprise bean class or method with a javax.ejb.TransactionAttribute annotation and setting it to one of the javax.ejb.TransactionAttributeType constants.

If you decorate the enterprise bean class with @TransactionAttribute, the specified TransactionAttributeType is applied to all the business methods in the class. Decorating a business method with @TransactionAttribute applies the TransactionAttributeType only to that method. If a @TransactionAttribute annotation decorates both the class and the method, the method TransactionAttributeType overrides the class TransactionAttributeType.

The TransactionAttributeType constants shown in Table 28–2 encapsulate the transaction attributes described earlier in this section.

Table 28–2 TransactionAttributeType Constants

Transaction Attribute 

TransactionAttributeType Constant

Required

TransactionAttributeType.REQUIRED

RequiresNew

TransactionAttributeType.REQUIRES_NEW

Mandatory

TransactionAttributeType.MANDATORY

NotSupported

TransactionAttributeType.NOT_SUPPORTED

Supports

TransactionAttributeType.SUPPORTS

Never

TransactionAttributeType.NEVER

The following code snippet demonstrates how to use the @TransactionAttribute annotation:

@TransactionAttribute(NOT_SUPPORTED)
@Stateful
public class TransactionBean implements Transaction {
...
    @TransactionAttribute(REQUIRES_NEW)
    public void firstMethod() {...}

    @TransactionAttribute(REQUIRED)
    public void secondMethod() {...}

    public void thirdMethod() {...}

    public void fourthMethod() {...}
}

In this example, the TransactionBean class’s transaction attribute has been set to NotSupported, firstMethod has been set to RequiresNew, and secondMethod has been set to Required. Because a @TransactionAttribute set on a method overrides the class @TransactionAttribute, calls to firstMethod will create a new transaction, and calls to secondMethod will either run in the current transaction or start a new transaction. Calls to thirdMethod or fourthMethod do not take place within a transaction.

Rolling Back a Container-Managed Transaction

There are two ways to roll back a container-managed transaction. First, if a system exception is thrown, the container will automatically roll back the transaction. Second, by invoking the setRollbackOnly method of the EJBContext interface, the bean method instructs the container to roll back the transaction. If the bean throws an application exception, the rollback is not automatic but can be initiated by a call to setRollbackOnly.

Synchronizing a Session Bean’s Instance Variables

The SessionSynchronization interface, which is optional, allows stateful session bean instances to receive transaction synchronization notifications. For example, you could synchronize the instance variables of an enterprise bean with their corresponding values in the database. The container invokes the SessionSynchronization methods (afterBegin, beforeCompletion, and afterCompletion) at each of the main stages of a transaction.

The afterBegin method informs the instance that a new transaction has begun. The container invokes afterBegin immediately before it invokes the business method.

The container invokes the beforeCompletion method after the business method has finished but just before the transaction commits. The beforeCompletion method is the last opportunity for the session bean to roll back the transaction (by calling setRollbackOnly).

The afterCompletion method indicates that the transaction has completed. This method has a single boolean parameter whose value is true if the transaction was committed and false if it was rolled back.

Methods Not Allowed in Container-Managed Transactions

You should not invoke any method that might interfere with the transaction boundaries set by the container. The list of prohibited methods follows:

You can, however, use these methods to set boundaries in application-managed transactions.

Bean-Managed Transactions

In bean-managed transaction demarcation, the code in the session or message-driven bean explicitly marks the boundaries of the transaction. Although beans with container-managed transactions require less coding, they have one limitation: When a method is executing, it can be associated with either a single transaction or no transaction at all. If this limitation will make coding your bean difficult, you should consider using bean-managed transactions.

The following pseudocode illustrates the kind of fine-grained control you can obtain with application-managed transactions. By checking various conditions, the pseudocode decides whether to start or stop certain transactions within the business method:

begin transaction
...
	update table-a
...
	if (condition-x)
   commit transaction
	else if (condition-y)
   update table-b
   commit transaction
	else
   rollback transaction
   begin transaction
   update table-c
   commit transaction

When coding an application-managed transaction for session or message-driven beans, you must decide whether to use Java Database Connectivity or JTA transactions. The sections that follow discuss both types of transactions.

JTA Transactions

JTA, or the Java Transaction API, allows you to demarcate transactions in a manner that is independent of the transaction manager implementation. GlassFish Server implements the transaction manager with the Java Transaction Service (JTS). However, your code doesn’t call the JTS methods directly but instead invokes the JTA methods, which then call the lower-level JTS routines.

A JTA transaction is controlled by the Java EE transaction manager. You may want to use a JTA transaction because it can span updates to multiple databases from different vendors. A particular DBMS’s transaction manager may not work with heterogeneous databases. However, the Java EE transaction manager does have one limitation: It does not support nested transactions. In other words, it cannot start a transaction for an instance until the preceding transaction has ended.

To demarcate a JTA transaction, you invoke the begin, commit, and rollback methods of the javax.transaction.UserTransaction interface.

Returning without Committing

In a stateless session bean with bean-managed transactions, a business method must commit or roll back a transaction before returning. However, a stateful session bean does not have this restriction.

In a stateful session bean with a JTA transaction, the association between the bean instance and the transaction is retained across multiple client calls. Even if each business method called by the client opens and closes the database connection, the association is retained until the instance completes the transaction.

In a stateful session bean with a JDBC transaction, the JDBC connection retains the association between the bean instance and the transaction across multiple calls. If the connection is closed, the association is not retained.

Methods Not Allowed in Bean-Managed Transactions

Do not invoke the getRollbackOnly and setRollbackOnly methods of the EJBContext interface in bean-managed transactions. These methods should be used only in container-managed transactions. For bean-managed transactions, invoke the getStatus and rollback methods of the UserTransaction interface.

Transaction Timeouts

For container-managed transactions, you can use the Administration Console to configure the transaction timeout interval. See Starting the Administration Console.

For enterprise beans with bean-managed JTA transactions, you invoke the setTransactionTimeout method of the UserTransaction interface.

ProcedureTo Set a Transaction Timeout

  1. In the Administration Console, expand the Configuration node and select Transaction Service.

  2. On the Transaction Service page, set the value of the Transaction Timeout field to the value of your choice (for example, 5).

    With this setting, if the transaction has not completed within 5 seconds, the EJB container rolls it back.

    The default value is 0, meaning that the transaction will not time out.

  3. Click Save.

Updating Multiple Databases

The Java EE transaction manager controls all enterprise bean transactions except for bean-managed JDBC transactions. The Java EE transaction manager allows an enterprise bean to update multiple databases within a transaction. Figure 28–2 and Figure 28–3 show two scenarios for updating multiple databases in a single transaction.

In Figure 28–2, the client invokes a business method in Bean-A. The business method begins a transaction, updates Database X, updates Database Y, and invokes a business method in Bean-B. The second business method updates Database Z and returns control to the business method in Bean-A, which commits the transaction. All three database updates occur in the same transaction.

In Figure 28–3, the client calls a business method in Bean-A, which begins a transaction and updates Database X. Then Bean-A invokes a method in Bean-B, which resides in a remote Java EE server. The method in Bean-B updates Database Y. The transaction managers of the Java EE servers ensure that both databases are updated in the same transaction.

Figure 28–2 Updating Multiple Databases

A diagram showing Bean-A updating databases X and Y,
and Bean-B updating database Z.

Figure 28–3 Updating Multiple Databases across Java EE Servers

A diagram showing Bean-A on one Java EE server updating
database X, and Bean-B on another Java EE server updating database Y.

Transactions in Web Components

You can demarcate a transaction in a web component by using either the java.sql.Connection or the javax.transaction.UserTransaction interface. These are the same interfaces that a session bean with bean-managed transactions can use. Transactions demarcated with the UserTransaction interface are discussed in JTA Transactions.

Further Information about Transactions

For more information about transactions, see

Chapter 29 Resource Connections

Java EE components can access a wide variety of resources, including databases, mail sessions, Java Message Service objects, and URLs. The Java EE 6 platform provides mechanisms that allow you to access all these resources in a similar manner. This chapter explains how to get connections to several types of resources.

The following topics are addressed here:

Resources and JNDI Naming

In a distributed application, components need to access other components and resources, such as databases. For example, a servlet might invoke remote methods on an enterprise bean that retrieves information from a database. In the Java EE platform, the Java Naming and Directory Interface (JNDI) naming service enables components to locate other components and resources.

A resource is a program object that provides connections to systems, such as database servers and messaging systems. (A Java Database Connectivity resource is sometimes referred to as a data source.) Each resource object is identified by a unique, people-friendly name, called the JNDI name. For example, the JNDI name of the JDBC resource for the Java DB database that is shipped with the GlassFish Server is jdbc/__default.

An administrator creates resources in a JNDI namespace. In the GlassFish Server, you can use either the Administration Console or the asadmin command to create resources. Applications then use annotations to inject the resources. If an application uses resource injection, the GlassFish Server invokes the JNDI API, and the application is not required to do so. However, it is also possible for an application to locate resources by making direct calls to the JNDI API.

A resource object and its JNDI name are bound together by the naming and directory service. To create a new resource, a new name/object binding is entered into the JNDI namespace. You inject resources by using the @Resource annotation in an application.

You can use a deployment descriptor to override the resource mapping that you specify in an annotation. Using a deployment descriptor allows you to change an application by repackaging it rather than by both recompiling the source files and repackaging. However, for most applications, a deployment descriptor is not necessary.

DataSource Objects and Connection Pools

To store, organize, and retrieve data, most applications use a relational database. Java EE 6 components may access relational databases through the JDBC API. For information on this API, see http://www.oracle.com/technetwork/java/javase/tech/index-jsp-136101.html.

In the JDBC API, databases are accessed by using DataSource objects. A DataSource has a set of properties that identify and describe the real-world data source that it represents. These properties include such information as the location of the database server, the name of the database, the network protocol to use to communicate with the server, and so on. In the GlassFish Server, a data source is called a JDBC resource.

Applications access a data source by using a connection, and a DataSource object can be thought of as a factory for connections to the particular data source that the DataSource instance represents. In a basic DataSource implementation, a call to the getConnection method returns a connection object that is a physical connection to the data source.

A DataSource object may be registered with a JNDI naming service. If so, an application can use the JNDI API to access that DataSource object, which can then be used to connect to the data source it represents.

DataSource objects that implement connection pooling also produce a connection to the particular data source that the DataSource class represents. The connection object that the getConnection method returns is a handle to a PooledConnection object rather than being a physical connection. An application uses the connection object in the same way that it uses a connection. Connection pooling has no effect on application code except that a pooled connection, like all connections, should always be explicitly closed. When an application closes a connection that is pooled, the connection is returned to a pool of reusable connections. The next time getConnection is called, a handle to one of these pooled connections will be returned if one is available. Because connection pooling avoids creating a new physical connection every time one is requested, applications can run significantly faster.

A JDBC connection pool is a group of reusable connections for a particular database. Because creating each new physical connection is time consuming, the server maintains a pool of available connections to increase performance. When it requests a connection, an application obtains one from the pool. When an application closes a connection, the connection is returned to the pool.

Applications that use the Persistence API specify the DataSource object they are using in the jta-data-source element of the persistence.xml file:

<jta-data-source>jdbc/MyOrderDB</jta-data-source>

This is typically the only reference to a JDBC object for a persistence unit. The application code does not refer to any JDBC objects.

Resource Injection

The javax.annotation.Resource annotation is used to declare a reference to a resource; @Resource can decorate a class, a field, or a method. The container will inject the resource referred to by @Resource into the component either at runtime or when the component is initialized, depending on whether field/method injection or class injection is used. With field-based and method-based injection, the container will inject the resource when the application is initialized. For class-based injection, the resource is looked up by the application at runtime.

The @Resource annotation has the following elements:

The name element is the JNDI name of the resource and is optional for field-based and method-based injection. For field-based injection, the default name is the field name qualified by the class name. For method-based injection, the default name is the JavaBeans property name, based on the method qualified by the class name. The name element must be specified for class-based injection.

The type of resource is determined by one of the following:

For class-based injection, the type element is required.

The authenticationType element is used only for connection factory resources, such as the resources of a connector, also called the resource adapter, or data source. This element can be set to one of the javax.annotation.Resource.AuthenticationType enumerated type values: CONTAINER, the default, and APPLICATION.

The shareable element is used only for Object Resource Broker (ORB) instance resources or connection factory resource. This element indicates whether the resource can be shared between this component and other components and may be set to true, the default, or false.

The mappedName element is a nonportable, implementation-specific name to which the resource should be mapped. Because the name element, when specified or defaulted, is local only to the application, many Java EE servers provide a way of referring to resources across the application server. This is done by setting the mappedName element. Use of the mappedName element is nonportable across Java EE server implementations.

The description element is the description of the resource, typically in the default language of the system on which the application is deployed. This element is used to help identify resources and to help application developers choose the correct resource.

Field-Based Injection

To use field-based resource injection, declare a field and decorate it with the @Resource annotation. The container will infer the name and type of the resource if the name and type elements are not specified. If you do specify the type element, it must match the field’s type declaration.

In the following code, the container infers the name of the resource, based on the class name and the field name: com.example.SomeClass/myDB. The inferred type is javax.sql.DataSource.class:

package com.example;

public class SomeClass {
    @Resource
    private javax.sql.DataSource myDB;
...
}

In the following code, the JNDI name is customerDB, and the inferred type is javax.sql.DataSource.class:

package com.example;

public class SomeClass {
    @Resource(name="customerDB")
    private javax.sql.DataSource myDB;
...
}

Method-Based Injection

To use method-based injection, declare a setter method and decorate it with the @Resource annotation. The container will infer the name and type of the resource if the name and type elements are not specified. The setter method must follow the JavaBeans conventions for property names: The method name must begin with set, have a void return type, and only one parameter. If you do specify the type element, it must match the field’s type declaration.

In the following code, the container infers the name of the resource based on the class name and the field name: com.example.SomeClass/myDB. The inferred type is javax.sql.DataSource.class:

package com.example;

public class SomeClass {

    private javax.sql.DataSource myDB;
...
    @Resource
    private void setMyDB(javax.sql.DataSource ds) {
        myDB = ds;
    }
...
}

In the following code, the JNDI name is customerDB, and the inferred type is javax.sql.DataSource.class:

package com.example;

public class SomeClass {

    private javax.sql.DataSource myDB;
...
    @Resource(name="customerDB")
    private void setMyDB(javax.sql.DataSource ds) {
        myDB = ds;
    }
...
}

Class-Based Injection

To use class-based injection, decorate the class with a @Resource annotation, and set the required name and type elements:

@Resource(name="myMessageQueue",
                type="javax.jms.ConnectionFactory")
public class SomeMessageBean {
...
}

The @Resources annotation is used to group together multiple @Resource declarations for class-based injection. The following code shows the @Resources annotation containing two @Resource declarations. One is a Java Message Service message queue, and the other is a JavaMail session:

@Resources({
    @Resource(name="myMessageQueue",
                    type="javax.jms.ConnectionFactory"),
    @Resource(name="myMailSession",
                    type="javax.mail.Session")
})
public class SomeMessageBean {
...
}

Resource Adapters and Contracts

A resource adapter is a Java EE component that implements the Java EE Connector Architecture for a specific EIS. Examples of EISs include enterprise resource planning, mainframe transaction processing, and database systems. As illustrated in Figure 29–1, the resource adapter facilitates communication between a Java EE application and an EIS.

Figure 29–1 Resource Adapters

Resource Adapter Contracts

Stored in a Resource Adapter Archive (RAR) file, a resource adapter can be deployed on any Java EE server, much like a Java EE application. A RAR file may be contained in an Enterprise Archive (EAR) file, or it may exist as a separate file.

A resource adapter is analogous to a JDBC driver. Both provide a standard API through which an application can access a resource that is outside the Java EE server. For a resource adapter, the target system is an EIS; for a JDBC driver, it is a DBMS. Resource adapters and JDBC drivers are rarely created by application developers. In most cases, both types of software are built by vendors that sell tools, servers, or integration software.

The resource adapter mediates communication between the Java EE server and the EIS by means of contracts. The application contract defines the API through which a Java EE component, such as an enterprise bean, accesses the EIS. This API is the only view that the component has of the EIS. The system contracts link the resource adapter to important services that are managed by the Java EE server. The resource adapter itself and its system contracts are transparent to the Java EE component.

Management Contracts

The Java EE Connector Architecture defines system contracts that enable resource adapter lifecycle and thread management.

Lifecycle Management

The Connector Architecture specifies a lifecycle management contract that allows an application server to manage the lifecycle of a resource adapter. This contract provides a mechanism for the application server to bootstrap a resource adapter instance during the deployment or application server startup. This contract also provides a means for the application server to notify the resource adapter instance when it is undeployed or when an orderly shutdown of the application server takes place.

Work Management Contract

The Connector Architecture work management contract ensures that resource adapters use threads in the proper, recommended manner. This contract also enables an application server to manage threads for resource adapters.

Resource adapters that improperly use threads can jeopardize the entire application server environment. For example, a resource adapter might create too many threads or might not properly release threads it has created. Poor thread handling inhibits application server shutdown and impacts the application server’s performance because creating and destroying threads are expensive operations.

The work management contract establishes a means for the application server to pool and reuse threads, similar to pooling and reusing connections. By adhering to this contract, the resource adapter does not have to manage threads itself. Instead, the resource adapter has the application server create and provide needed threads. When it is finished with a given thread, the resource adapter returns the thread to the application server. The application server manages the thread, either returning it to a pool for later reuse or destroying it. Handling threads in this manner results in increased application server performance and more efficient use of resources.

In addition to moving thread management to the application server, the Connector Architecture provides a flexible model for a resource adapter that uses threads.

With the latter two approaches, the submitting thread and the work thread may execute simultaneously or independently. For these approaches, the contract specifies a listener mechanism to notify the resource adapter that the thread has completed its operation. The resource adapter can also specify the execution context for the thread, and the work management contract controls the context in which the thread executes.

Generic Work Context Contract

The work management contract between the application server and a resource adapter enables a resource adapter to do a task, such as communicating with the EIS or delivering messages, by delivering Work instances for execution.

A generic work context contract enables a resource adapter to control the contexts in which the Work instances that it submits are executed by the application server’s WorkManager. A generic work context mechanism also enables an application server to support new message inflow and delivery schemes. It also provides a richer contextual Work execution environment to the resource adapter while still maintaining control over concurrent behavior in a managed environment.

The generic work context contract standardizes the transaction context and the security context.

Outbound and Inbound Contracts

The Connector Architecture defines the following outbound contracts, system-level contracts between an application server and an EIS that enable outbound connectivity to an EIS.

Inbound contracts are system contracts between a Java EE server and an EIS that enable inbound connectivity from the EIS: pluggability contracts for message providers and contracts for importing transactions.

Metadata Annotations

Java EE Connector Architecture 1.6 introduces a set of annotations to minimize the need for deployment descriptors.

The specification allows a resource adapter to be developed in mixed-mode form, that is the ability for a resource adapter developer to use both metadata annotations and deployment descriptors in applications. An application assembler or deployer may use the deployment descriptor to override the metadata annotations specified by the resource adapter developer.

The deployment descriptor for a resource adapter is named ra.xml. The metadata-complete attribute defines whether the deployment descriptor for the resource adapter module is complete or whether the class files available to the module and packaged with the resource adapter need to be examined for annotations that specify deployment information.

For the complete list of annotations and JavaBeans components introduced in the Java EE 6 platform, see the Java EE Connector Architecture 1.6 specification.

Common Client Interface

This section explains how components use the Connector Architecture Common Client Interface (CCI) API and a resource adapter to access data from an EIS. The CCI API defines a set of interfaces and classes whose methods allow a client to perform typical data access operations. The CCI interfaces and classes are as follows:

A client or application component that uses the CCI to interact with an underlying EIS does so in a prescribed manner. The component must establish a connection to the EIS’s resource manager, and it does so using the ConnectionFactory. The Connection object represents the connection to the EIS and is used for subsequent interactions with the EIS.

The component performs its interactions with the EIS, such as accessing data from a specific table, using an Interaction object. The application component defines the Interaction object by using an InteractionSpec object. When it reads data from the EIS, such as from database tables, or writes to those tables, the application component does so by using a particular type of Record instance: a MappedRecord, an IndexedRecord, or a ResultSet instance.

Note, too, that a client application that relies on a CCI resource adapter is very much like any other Java EE client that uses enterprise bean methods.

Further Information about Resources

For more information about resources and annotations, see

Chapter 30 Java Message Service Concepts

This chapter provides an introduction to the Java Message Service (JMS) API, a Java API that allows applications to create, send, receive, and read messages using reliable, asynchronous, loosely coupled communication. It covers the following topics:

Overview of the JMS API

This overview of the JMS API answers the following questions.

What Is Messaging?

Messaging is a method of communication between software components or applications. A messaging system is a peer-to-peer facility: A messaging client can send messages to, and receive messages from, any other client. Each client connects to a messaging agent that provides facilities for creating, sending, receiving, and reading messages.

Messaging enables distributed communication that is loosely coupled. A component sends a message to a destination, and the recipient can retrieve the message from the destination. However, the sender and the receiver do not have to be available at the same time in order to communicate. In fact, the sender does not need to know anything about the receiver; nor does the receiver need to know anything about the sender. The sender and the receiver need to know only which message format and which destination to use. In this respect, messaging differs from tightly coupled technologies, such as Remote Method Invocation (RMI), which require an application to know a remote application’s methods.

Messaging also differs from electronic mail (email), which is a method of communication between people or between software applications and people. Messaging is used for communication between software applications or software components.

What Is the JMS API?

The Java Message Service is a Java API that allows applications to create, send, receive, and read messages. Designed by Sun and several partner companies, the JMS API defines a common set of interfaces and associated semantics that allow programs written in the Java programming language to communicate with other messaging implementations.

The JMS API minimizes the set of concepts a programmer must learn in order to use messaging products but provides enough features to support sophisticated messaging applications. It also strives to maximize the portability of JMS applications across JMS providers in the same messaging domain.

The JMS API enables communication that is not only loosely coupled but also

The JMS specification was first published in August 1998. The latest version is Version 1.1, which was released in April 2002. You can download a copy of the specification from the JMS web site: http://www.oracle.com/technetwork/java/index-jsp-142945.html.

When Can You Use the JMS API?

An enterprise application provider is likely to choose a messaging API over a tightly coupled API, such as remote procedure call (RPC), under the following circumstances.

For example, components of an enterprise application for an automobile manufacturer can use the JMS API in situations like these:

Using messaging for these tasks allows the various components to interact with one another efficiently, without tying up network or other resources. Figure 30–1 illustrates how this simple example might work.

Figure 30–1 Messaging in an Enterprise Application

Diagram showing messaging between various departments
in an enterprise

Manufacturing is only one example of how an enterprise can use the JMS API. Retail applications, financial services applications, health services applications, and many others can make use of messaging.

How Does the JMS API Work with the Java EE Platform?

When the JMS API was introduced in 1998, its most important purpose was to allow Java applications to access existing messaging-oriented middleware (MOM) systems, such as MQSeries from IBM. Since that time, many vendors have adopted and implemented the JMS API, so a JMS product can now provide a complete messaging capability for an enterprise.

Beginning with the 1.3 release of the Java EE platform, the JMS API has been an integral part of the platform, and application developers can use messaging with Java EE components.

The JMS API in the Java EE platform has the following features.

The JMS API enhances the Java EE platform by simplifying enterprise development, allowing loosely coupled, reliable, asynchronous interactions among Java EE components and legacy systems capable of messaging. A developer can easily add new behavior to a Java EE application that has existing business events by adding a new message-driven bean to operate on specific business events. The Java EE platform, moreover, enhances the JMS API by providing support for distributed transactions and allowing for the concurrent consumption of messages. For more information, see the Enterprise JavaBeans specification, v3.1.

The JMS provider can be integrated with the application server using the Java EE Connector architecture. You access the JMS provider through a resource adapter. This capability allows vendors to create JMS providers that can be plugged in to multiple application servers, and it allows application servers to support multiple JMS providers. For more information, see the Java EE Connector architecture specification, v1.6.

Basic JMS API Concepts

This section introduces the most basic JMS API concepts, the ones you must know to get started writing simple application clients that use the JMS API.

The next section introduces the JMS API programming model. Later sections cover more advanced concepts, including the ones you need to write applications that use message-driven beans.

JMS API Architecture

A JMS application is composed of the following parts.

Figure 30–2 illustrates the way these parts interact. Administrative tools allow you to bind destinations and connection factories into a JNDI namespace. A JMS client can then use resource injection to access the administered objects in the namespace and then establish a logical connection to the same objects through the JMS provider.

Figure 30–2 JMS API Architecture

Diagram of JMS API architecture, showing administrative
tool, JMS client, JNDI namespace, and JMS provider

Messaging Domains

Before the JMS API existed, most messaging products supported either the point-to-point or the publish/subscribe approach to messaging. The JMS specification provides a separate domain for each approach and defines compliance for each domain. A stand-alone JMS provider can implement one or both domains. A Java EE provider must implement both domains.

In fact, most implementations of the JMS API support both the point-to-point and the publish/subscribe domains, and some JMS clients combine the use of both domains in a single application. In this way, the JMS API has extended the power and flexibility of messaging products.

The JMS specification goes one step further: It provides common interfaces that enable you to use the JMS API in a way that is not specific to either domain. The following subsections describe the two messaging domains and then describe the use of the common interfaces.

Point-to-Point Messaging Domain

A point-to-point (PTP) product or application is built on the concept of message queues, senders, and receivers. Each message is addressed to a specific queue, and receiving clients extract messages from the queues established to hold their messages. Queues retain all messages sent to them until the messages are consumed or until the messages expire.

PTP messaging has the following characteristics and is illustrated in Figure 30–3.

Figure 30–3 Point-to-Point Messaging

Diagram of point-to-point messaging, showing Client 1
sending a message to a queue, and Client 2 consuming and acknowledging the
message

Use PTP messaging when every message you send must be processed successfully by one consumer.

Publish/Subscribe Messaging Domain

In a publish/subscribe (pub/sub) product or application, clients address messages to a topic, which functions somewhat like a bulletin board. Publishers and subscribers are generally anonymous and can dynamically publish or subscribe to the content hierarchy. The system takes care of distributing the messages arriving from a topic’s multiple publishers to its multiple subscribers. Topics retain messages only as long as it takes to distribute them to current subscribers.

Pub/sub messaging has the following characteristics.

The JMS API relaxes this timing dependency to some extent by allowing subscribers to create durable subscriptions, which receive messages sent while the subscribers are not active. Durable subscriptions provide the flexibility and reliability of queues but still allow clients to send messages to many recipients. For more information about durable subscriptions, see Creating Durable Subscriptions.

Use pub/sub messaging when each message can be processed by zero, one, or many consumers. Figure 30–4 illustrates pub/sub messaging.

Figure 30–4 Publish/Subscribe Messaging

Diagram of pub/sub messaging, showing Client 1 publishing
a message to a topic, and the message being delivered to two subscribers to
the topic

Programming with the Common Interfaces

Version 1.1 of the JMS API allows you to use the same code to send and receive messages under either the PTP or the pub/sub domain. The destinations that you use remain domain-specific, and the behavior of the application will depend in part on whether you are using a queue or a topic. However, the code itself can be common to both domains, making your applications flexible and reusable. This tutorial describes and illustrates these common interfaces.

Message Consumption

Messaging products are inherently asynchronous: There is no fundamental timing dependency between the production and the consumption of a message. However, the JMS specification uses this term in a more precise sense. Messages can be consumed in either of two ways:

The JMS API Programming Model

The basic building blocks of a JMS application consist of

Figure 30–5 shows how all these objects fit together in a JMS client application.

Figure 30–5 The JMS API Programming Model

Diagram of the JMS API programming model: connection
factory, connection, session, message producer, message consumer, messages,
and destinations

This section describes all these objects briefly and provides sample commands and code snippets that show how to create and use the objects. The last subsection briefly describes JMS API exception handling.

Examples that show how to combine all these objects in applications appear in later sections. For more details, see the JMS API documentation, which is part of the Java EE API documentation.

JMS Administered Objects

Two parts of a JMS application, destinations and connection factories, are best maintained administratively rather than programmatically. The technology underlying these objects is likely to be very different from one implementation of the JMS API to another. Therefore, the management of these objects belongs with other administrative tasks that vary from provider to provider.

JMS clients access these objects through interfaces that are portable, so a client application can run with little or no change on more than one implementation of the JMS API. Ordinarily, an administrator configures administered objects in a JNDI namespace, and JMS clients then access them by using resource injection.

With Oracle GlassFish Server, you use the asadmin command or the Administration Console to create JMS administered objects in the form of resources.

JMS Connection Factories

A connection factory is the object a client uses to create a connection to a provider. A connection factory encapsulates a set of connection configuration parameters that has been defined by an administrator. Each connection factory is an instance of the ConnectionFactory, QueueConnectionFactory, or TopicConnectionFactory interface. To learn how to create connection factories, see To Create JMS Administered Objects for the Synchronous Receive Example.

At the beginning of a JMS client program, you usually inject a connection factory resource into a ConnectionFactory object. For example, the following code fragment specifies a resource whose JNDI name is jms/ConnectionFactory and assigns it to a ConnectionFactory object:

@Resource(lookup = "jms/ConnectionFactory")
private static ConnectionFactory connectionFactory;

In a Java EE application, JMS administered objects are normally placed in the jms naming subcontext.

JMS Destinations

A destination is the object a client uses to specify the target of messages it produces and the source of messages it consumes. In the PTP messaging domain, destinations are called queues. In the pub/sub messaging domain, destinations are called topics. A JMS application can use multiple queues or topics (or both). To learn how to create destination resources, see To Create JMS Administered Objects for the Synchronous Receive Example.

To create a destination using the GlassFish Server, you create a JMS destination resource that specifies a JNDI name for the destination.

In the GlassFish Server implementation of JMS, each destination resource refers to a physical destination. You can create a physical destination explicitly, but if you do not, the Application Server creates it when it is needed and deletes it when you delete the destination resource.

In addition to injecting a connection factory resource into a client program, you usually inject a destination resource. Unlike connection factories, destinations are specific to one domain or the other. To create an application that allows you to use the same code for both topics and queues, you assign the destination to a Destination object.

The following code specifies two resources, a queue and a topic. The resource names are mapped to destination resources created in the JNDI namespace.

@Resource(lookup = "jms/Queue")
private static Queue queue;

@Resource(lookup = "jms/Topic")
private static Topic topic;

With the common interfaces, you can mix or match connection factories and destinations. That is, in addition to using the ConnectionFactory interface, you can inject a QueueConnectionFactory resource and use it with a Topic, and you can inject a TopicConnectionFactory resource and use it with a Queue. The behavior of the application will depend on the kind of destination you use and not on the kind of connection factory you use.

JMS Connections

A connection encapsulates a virtual connection with a JMS provider. A connection could represent an open TCP/IP socket between a client and a provider service daemon. You use a connection to create one or more sessions.

Connections implement the Connection interface. When you have a ConnectionFactory object, you can use it to create a Connection:

Connection connection = connectionFactory.createConnection();

Before an application completes, you must close any connections that you have created. Failure to close a connection can cause resources not to be released by the JMS provider. Closing a connection also closes its sessions and their message producers and message consumers.

connection.close();

Before your application can consume messages, you must call the connection’s start method; for details, see JMS Message Consumers. If you want to stop message delivery temporarily without closing the connection, you call the stop method.

JMS Sessions

A session is a single-threaded context for producing and consuming messages. You use sessions to create the following:

Sessions serialize the execution of message listeners; for details, see JMS Message Listeners.

A session provides a transactional context with which to group a set of sends and receives into an atomic unit of work. For details, see Using JMS API Local Transactions.

Sessions implement the Session interface. After you create a Connection object, you use it to create a Session:

Session session = connection.createSession(false,
     Session.AUTO_ACKNOWLEDGE);

The first argument means that the session is not transacted; the second means that the session automatically acknowledges messages when they have been received successfully. (For more information, see Controlling Message Acknowledgment.)

To create a transacted session, use the following code:

Session session = connection.createSession(true, 0);

Here, the first argument means that the session is transacted; the second indicates that message acknowledgment is not specified for transacted sessions. For more information on transactions, see Using JMS API Local Transactions. For information about the way JMS transactions work in Java EE applications, see Using the JMS API in Java EE Applications.

JMS Message Producers

A message producer is an object that is created by a session and used for sending messages to a destination. It implements the MessageProducer interface.

You use a Session to create a MessageProducer for a destination. The following examples show that you can create a producer for a Destination object, a Queue object, or a Topic object:

MessageProducer producer = session.createProducer(dest);
MessageProducer producer = session.createProducer(queue);
MessageProducer producer = session.createProducer(topic);

You can create an unidentified producer by specifying null as the argument to createProducer. With an unidentified producer, you do not specify a destination until you send a message.

After you have created a message producer, you can use it to send messages by using the send method:

producer.send(message);

You must first create the messages; see JMS Messages.

If you created an unidentified producer, use an overloaded send method that specifies the destination as the first parameter. For example:

MessageProducer anon_prod = session.createProducer(null);
anon_prod.send(dest, message);

JMS Message Consumers

A message consumer is an object that is created by a session and used for receiving messages sent to a destination. It implements the MessageConsumer interface.

A message consumer allows a JMS client to register interest in a destination with a JMS provider. The JMS provider manages the delivery of messages from a destination to the registered consumers of the destination.

For example, you could use a Session to create a MessageConsumer for a Destination object, a Queue object, or a Topic object:

MessageConsumer consumer = session.createConsumer(dest);
MessageConsumer consumer = session.createConsumer(queue);
MessageConsumer consumer = session.createConsumer(topic);

You use the Session.createDurableSubscriber method to create a durable topic subscriber. This method is valid only if you are using a topic. For details, see Creating Durable Subscriptions.

After you have created a message consumer, it becomes active, and you can use it to receive messages. You can use the close method for a MessageConsumer to make the message consumer inactive. Message delivery does not begin until you start the connection you created by calling its start method. (Remember always to call the start method; forgetting to start the connection is one of the most common JMS programming errors.)

You use the receive method to consume a message synchronously. You can use this method at any time after you call the start method:

connection.start();
Message m = consumer.receive();
connection.start();
Message m = consumer.receive(1000); // time out after a second

To consume a message asynchronously, you use a message listener, described in the next section.

JMS Message Listeners

A message listener is an object that acts as an asynchronous event handler for messages. This object implements the MessageListener interface, which contains one method, onMessage. In the onMessage method, you define the actions to be taken when a message arrives.

You register the message listener with a specific MessageConsumer by using the setMessageListener method. For example, if you define a class named Listener that implements the MessageListener interface, you can register the message listener as follows:

Listener myListener = new Listener();
consumer.setMessageListener(myListener);

After you register the message listener, you call the start method on the Connection to begin message delivery. (If you call start before you register the message listener, you are likely to miss messages.)

When message delivery begins, the JMS provider automatically calls the message listener’s onMessage method whenever a message is delivered. The onMessage method takes one argument of type Message, which your implementation of the method can cast to any of the other message types (see Message Bodies).

A message listener is not specific to a particular destination type. The same listener can obtain messages from either a queue or a topic, depending on the type of destination for which the message consumer was created. A message listener does, however, usually expect a specific message type and format.

Your onMessage method should handle all exceptions. It must not throw checked exceptions, and throwing a RuntimeException is considered a programming error.

The session used to create the message consumer serializes the execution of all message listeners registered with the session. At any time, only one of the session’s message listeners is running.

In the Java EE platform, a message-driven bean is a special kind of message listener. For details, see Using Message-Driven Beans to Receive Messages Asynchronously.

JMS Message Selectors

If your messaging application needs to filter the messages it receives, you can use a JMS API message selector, which allows a message consumer to specify the messages it is interested in. Message selectors assign the work of filtering messages to the JMS provider rather than to the application. For an example of an application that uses a message selector, see An Application That Uses the JMS API with a Session Bean.

A message selector is a String that contains an expression. The syntax of the expression is based on a subset of the SQL92 conditional expression syntax. The message selector in the example selects any message that has a NewsType property that is set to the value 'Sports' or 'Opinion':

NewsType = ’Sports’ OR NewsType = ’Opinion’

The createConsumer and createDurableSubscriber methods allow you to specify a message selector as an argument when you create a message consumer.

The message consumer then receives only messages whose headers and properties match the selector. (See Message Headers, and Message Properties.) A message selector cannot select messages on the basis of the content of the message body.

JMS Messages

The ultimate purpose of a JMS application is to produce and to consume messages that can then be used by other software applications. JMS messages have a basic format that is simple but highly flexible, allowing you to create messages that match formats used by non-JMS applications on heterogeneous platforms.

A JMS message has three parts: a header, properties, and a body. Only the header is required. The following sections describe these parts.

For complete documentation of message headers, properties, and bodies, see the documentation of the Message interface in the API documentation.

Message Headers

A JMS message header contains a number of predefined fields that contain values that both clients and providers use to identify and to route messages. Table 30–1 lists the JMS message header fields and indicates how their values are set. For example, every message has a unique identifier, which is represented in the header field JMSMessageID. The value of another header field, JMSDestination, represents the queue or the topic to which the message is sent. Other fields include a timestamp and a priority level.

Each header field has associated setter and getter methods, which are documented in the description of the Message interface. Some header fields are intended to be set by a client, but many are set automatically by the send or the publish method, which overrides any client-set values.

Table 30–1 How JMS Message Header Field Values Are Set

Header Field 

Set By 

JMSDestination

send or publish method

JMSDeliveryMode

send or publish method

JMSExpiration

send or publish method

JMSPriority

send or publish method

JMSMessageID

send or publish method

JMSTimestamp

send or publish method

JMSCorrelationID

Client 

JMSReplyTo

Client 

JMSType

Client 

JMSRedelivered

JMS provider 

Message Properties

You can create and set properties for messages if you need values in addition to those provided by the header fields. You can use properties to provide compatibility with other messaging systems, or you can use them to create message selectors (see JMS Message Selectors). For an example of setting a property to be used as a message selector, see An Application That Uses the JMS API with a Session Bean.

The JMS API provides some predefined property names that a provider can support. The use either of these predefined properties or of user-defined properties is optional.

Message Bodies

The JMS API defines five message body formats, also called message types, which allow you to send and to receive data in many different forms and provide compatibility with existing messaging formats. Table 30–2 describes these message types.

Table 30–2 JMS Message Types

Message Type 

Body Contains 

TextMessage

A java.lang.String object (for example, the contents of an XML file).

MapMessage

A set of name-value pairs, with names as String objects and values as primitive types in the Java programming language. The entries can be accessed sequentially by enumerator or randomly by name. The order of the entries is undefined.

BytesMessage

A stream of uninterpreted bytes. This message type is for literally encoding a body to match an existing message format. 

StreamMessage

A stream of primitive values in the Java programming language, filled and read sequentially. 

ObjectMessage

A Serializable object in the Java programming language.

Message

Nothing. Composed of header fields and properties only. This message type is useful when a message body is not required. 

The JMS API provides methods for creating messages of each type and for filling in their contents. For example, to create and send a TextMessage, you might use the following statements:

TextMessage message = session.createTextMessage();
message.setText(msg_text);     // msg_text is a String
producer.send(message);

At the consuming end, a message arrives as a generic Message object and must be cast to the appropriate message type. You can use one or more getter methods to extract the message contents. The following code fragment uses the getText method:

Message m = consumer.receive();
if (m instanceof TextMessage) {
    TextMessage message = (TextMessage) m;
    System.out.println("Reading message: " + message.getText());
} else {
    // Handle error
}

JMS Queue Browsers

You can create a QueueBrowser object to inspect the messages in a queue. Messages sent to a queue remain in the queue until the message consumer for that queue consumes them. Therefore, the JMS API provides an object that allows you to browse the messages in the queue and display the header values for each message. To create a QueueBrowser object, use the Session.createBrowser method. For example:

QueueBrowser browser = session.createBrowser(queue);

See A Simple Example of Browsing Messages in a Queue for an example of the use of a QueueBrowser object.

The createBrowser method allows you to specify a message selector as a second argument when you create a QueueBrowser. For information on message selectors, see JMS Message Selectors.

The JMS API provides no mechanism for browsing a topic. Messages usually disappear from a topic as soon as they appear: if there are no message consumers to consume them, the JMS provider removes them. Although durable subscriptions allow messages to remain on a topic while the message consumer is not active, no facility exists for examining them.

JMS Exception Handling

The root class for exceptions thrown by JMS API methods is JMSException. Catching JMSException provides a generic way of handling all exceptions related to the JMS API.

The JMSException class includes the following subclasses, which are described in the API documentation:

All the examples in the tutorial catch and handle JMSException when it is appropriate to do so.

Creating Robust JMS Applications

This section explains how to use features of the JMS API to achieve the level of reliability and performance your application requires. Many people choose to implement JMS applications because they cannot tolerate dropped or duplicate messages and require that every message be received once and only once. The JMS API provides this functionality.

The most reliable way to produce a message is to send a PERSISTENT message within a transaction. JMS messages are PERSISTENT by default. A transaction is a unit of work into which you can group a series of operations, such as message sends and receives, so that the operations either all succeed or all fail. For details, see Specifying Message Persistence and Using JMS API Local Transactions.

The most reliable way to consume a message is to do so within a transaction, either from a queue or from a durable subscription to a topic. For details, see Creating Temporary Destinations, Creating Durable Subscriptions, and Using JMS API Local Transactions.

For other applications, a lower level of reliability can reduce overhead and improve performance. You can send messages with varying priority levels (see Setting Message Priority Levels) and you can set them to expire after a certain length of time (see Allowing Messages to Expire).

The JMS API provides several ways to achieve various kinds and degrees of reliability. This section divides them into two categories, basic and advanced.

The following sections describe these features as they apply to JMS clients. Some of the features work differently in Java EE applications; in these cases, the differences are noted here and are explained in detail in Using the JMS API in Java EE Applications.

Using Basic Reliability Mechanisms

The basic mechanisms for achieving or affecting reliable message delivery are as follows:

Controlling Message Acknowledgment

    Until a JMS message has been acknowledged, it is not considered to be successfully consumed. The successful consumption of a message ordinarily takes place in three stages.

  1. The client receives the message.

  2. The client processes the message.

  3. The message is acknowledged. Acknowledgment is initiated either by the JMS provider or by the client, depending on the session acknowledgment mode.

In transacted sessions (see Using JMS API Local Transactions), acknowledgment happens automatically when a transaction is committed. If a transaction is rolled back, all consumed messages are redelivered.

In nontransacted sessions, when and how a message is acknowledged depend on the value specified as the second argument of the createSession method. The three possible argument values are as follows:

If messages have been received from a queue but not acknowledged when a session terminates, the JMS provider retains them and redelivers them when a consumer next accesses the queue. The provider also retains unacknowledged messages for a terminated session that has a durable TopicSubscriber. (See Creating Durable Subscriptions.) Unacknowledged messages for a nondurable TopicSubscriber are dropped when the session is closed.

If you use a queue or a durable subscription, you can use the Session.recover method to stop a nontransacted session and restart it with its first unacknowledged message. In effect, the session’s series of delivered messages is reset to the point after its last acknowledged message. The messages it now delivers may be different from those that were originally delivered, if messages have expired or if higher-priority messages have arrived. For a nondurable TopicSubscriber, the provider may drop unacknowledged messages when its session is recovered.

The sample program in XREF the next section demonstrates two ways to ensure that a message will not be acknowledged until processing of the message is complete.

Specifying Message Persistence

The JMS API supports two delivery modes for messages to specify whether messages are lost if the JMS provider fails. These delivery modes are fields of the DeliveryMode interface.

You can specify the delivery mode in either of two ways.

If you do not specify a delivery mode, the default is PERSISTENT. Using the NON_PERSISTENT delivery mode may improve performance and reduce storage overhead, but you should use it only if your application can afford to miss messages.

Setting Message Priority Levels

You can use message priority levels to instruct the JMS provider to deliver urgent messages first. You can set the priority level in either of two ways.

The ten levels of priority range from 0 (lowest) to 9 (highest). If you do not specify a priority level, the default level is 4. A JMS provider tries to deliver higher-priority messages before lower-priority ones but does not have to deliver messages in exact order of priority.

Allowing Messages to Expire

By default, a message never expires. If a message will become obsolete after a certain period, however, you may want to set an expiration time. You can do this in either of two ways.

If the specified timeToLive value is 0, the message never expires.

When the message is sent, the specified timeToLive is added to the current time to give the expiration time. Any message not delivered before the specified expiration time is destroyed. The destruction of obsolete messages conserves storage and computing resources.

Creating Temporary Destinations

Normally, you create JMS destinations (queues and topics) administratively rather than programmatically. Your JMS provider includes a tool that you use to create and remove destinations, and it is common for destinations to be long-lasting.

The JMS API also enables you to create destinations (TemporaryQueue and TemporaryTopic objects) that last only for the duration of the connection in which they are created. You create these destinations dynamically using the Session.createTemporaryQueue and the Session.createTemporaryTopic methods.

The only message consumers that can consume from a temporary destination are those created by the same connection that created the destination. Any message producer can send to the temporary destination. If you close the connection that a temporary destination belongs to, the destination is closed and its contents are lost.

You can use temporary destinations to implement a simple request/reply mechanism. If you create a temporary destination and specify it as the value of the JMSReplyTo message header field when you send a message, then the consumer of the message can use the value of the JMSReplyTo field as the destination to which it sends a reply. The consumer can also reference the original request by setting the JMSCorrelationID header field of the reply message to the value of the JMSMessageID header field of the request. For example, an onMessage method can create a session so that it can send a reply to the message it receives. It can use code such as the following:

producer = session.createProducer(msg.getJMSReplyTo());
replyMsg = session.createTextMessage("Consumer " +
    "processed message: " + msg.getText());
replyMsg.setJMSCorrelationID(msg.getJMSMessageID());
producer.send(replyMsg);

For more examples, see Chapter 31, Java Message Service Examples.

Using Advanced Reliability Mechanisms

The more advanced mechanisms for achieving reliable message delivery are the following:

Creating Durable Subscriptions

To ensure that a pub/sub application receives all published messages, use PERSISTENT delivery mode for the publishers. In addition, use durable subscriptions for the subscribers.

The Session.createConsumer method creates a nondurable subscriber if a topic is specified as the destination. A nondurable subscriber can receive only messages that are published while it is active.

At the cost of higher overhead, you can use the Session.createDurableSubscriber method to create a durable subscriber. A durable subscription can have only one active subscriber at a time.

A durable subscriber registers a durable subscription by specifying a unique identity that is retained by the JMS provider. Subsequent subscriber objects that have the same identity resume the subscription in the state in which it was left by the preceding subscriber. If a durable subscription has no active subscriber, the JMS provider retains the subscription’s messages until they are received by the subscription or until they expire.

You establish the unique identity of a durable subscriber by setting the following:

You set the client ID administratively for a client-specific connection factory using either the command line or the Administration Console.

After using this connection factory to create the connection and the session, you call the createDurableSubscriber method with two arguments: the topic and a string that specifies the name of the subscription:

String subName = "MySub";
MessageConsumer topicSubscriber =
     session.createDurableSubscriber(myTopic, subName);

The subscriber becomes active after you start the Connection or TopicConnection. Later, you might close the subscriber:

topicSubscriber.close();

The JMS provider stores the messages sent or published to the topic, as it would store messages sent to a queue. If the program or another application calls createDurableSubscriber using the same connection factory and its client ID, the same topic, and the same subscription name, the subscription is reactivated, and the JMS provider delivers the messages that were published while the subscriber was inactive.

To delete a durable subscription, first close the subscriber, and then use the unsubscribe method, with the subscription name as the argument:

topicSubscriber.close();
session.unsubscribe("MySub");

The unsubscribe method deletes the state that the provider maintains for the subscriber.

Figure 30–6 and Figure 30–7 show the difference between a nondurable and a durable subscriber. With an ordinary, nondurable subscriber, the subscriber and the subscription begin and end at the same point and are, in effect, identical. When a subscriber is closed, the subscription also ends. Here, create stands for a call to Session.createConsumer with a Topic argument, and close stands for a call to MessageConsumer.close. Any messages published to the topic between the time of the first close and the time of the second create are not consumed by the subscriber. In Figure 30–6, the subscriber consumes messages M1, M2, M5, and M6, but messages M3 and M4 are lost.

Figure 30–6 Nondurable Subscribers and Subscriptions

Diagram showing messages being lost when nondurable subscriptions
are used

With a durable subscriber, the subscriber can be closed and re-created, but the subscription continues to exist and to hold messages until the application calls the unsubscribe method. In Figure 30–7, create stands for a call to Session.createDurableSubscriber, close stands for a call to MessageConsumer.close, and unsubscribe stands for a call to Session.unsubscribe. Messages published while the subscriber is closed are received when the subscriber is created again. So even though messages M2, M4, and M5 arrive while the subscriber is closed, they are not lost.

Figure 30–7 A Durable Subscriber and Subscription

Diagram showing messages being preserved when durable
subscriptions are used

See A Message Acknowledgment Example, A Durable Subscription Example, and An Application That Uses the JMS API with a Session Bean for examples of Java EE applications that use durable subscriptions.

Using JMS API Local Transactions

You can group a series of operations into an atomic unit of work called a transaction. If any one of the operations fails, the transaction can be rolled back, and the operations can be attempted again from the beginning. If all the operations succeed, the transaction can be committed.

In a JMS client, you can use local transactions to group message sends and receives. The JMS API Session interface provides commit and rollback methods that you can use in a JMS client. A transaction commit means that all produced messages are sent and all consumed messages are acknowledged. A transaction rollback means that all produced messages are destroyed and all consumed messages are recovered and redelivered unless they have expired (see Allowing Messages to Expire).

A transacted session is always involved in a transaction. As soon as the commit or the rollback method is called, one transaction ends and another transaction begins. Closing a transacted session rolls back its transaction in progress, including any pending sends and receives.

In an Enterprise JavaBeans component, you cannot use the Session.commit and Session.rollback methods. Instead, you use distributed transactions, which are described in Using the JMS API in Java EE Applications.

You can combine several sends and receives in a single JMS API local transaction. If you do so, you need to be careful about the order of the operations. You will have no problems if the transaction consists of all sends or all receives or if the receives come before the sends. But if you try to use a request/reply mechanism, whereby you send a message and then try to receive a reply to the sent message in the same transaction, the program will hang, because the send cannot take place until the transaction is committed. The following code fragment illustrates the problem:

// Don’t do this!
outMsg.setJMSReplyTo(replyQueue);
producer.send(outQueue, outMsg);
consumer = session.createConsumer(replyQueue);
inMsg = consumer.receive();
session.commit();

Because a message sent during a transaction is not actually sent until the transaction is committed, the transaction cannot contain any receives that depend on that message’s having been sent.

In addition, the production and the consumption of a message cannot both be part of the same transaction. The reason is that the transactions take place between the clients and the JMS provider, which intervenes between the production and the consumption of the message. Figure 30–8 illustrates this interaction.

Figure 30–8 Using JMS API Local Transactions

Diagram of local transactions, showing separate transactions
for sending and consuming a message

The sending of one or more messages to one or more destinations by client 1 can form a single transaction, because it forms a single set of interactions with the JMS provider using a single session. Similarly, the receiving of one or more messages from one or more destinations by client 2 also forms a single transaction using a single session. But because the two clients have no direct interaction and are using two different sessions, no transactions can take place between them.

Another way of putting this is that the act of producing and/or consuming messages in a session can be transactional, but the act of producing and consuming a specific message across different sessions cannot be transactional.

This is the fundamental difference between messaging and synchronized processing. Instead of tightly coupling the sending and receiving of data, message producers and consumers use an alternative approach to reliability, one that is built on a JMS provider’s ability to supply a once-and-only-once message delivery guarantee.

When you create a session, you specify whether it is transacted. The first argument to the createSession method is a boolean value. A value of true means that the session is transacted; a value of false means that it is not transacted. The second argument to this method is the acknowledgment mode, which is relevant only to nontransacted sessions (see Controlling Message Acknowledgment). If the session is transacted, the second argument is ignored, so it is a good idea to specify 0 to make the meaning of your code clear. For example:

session = connection.createSession(true, 0);

The commit and the rollback methods for local transactions are associated with the session. You can combine queue and topic operations in a single transaction if you use the same session to perform the operations. For example, you can use the same session to receive a message from a queue and send a message to a topic in the same transaction.

You can pass a client program’s session to a message listener’s constructor function and use it to create a message producer. In this way, you can use the same session for receives and sends in asynchronous message consumers.

A Local Transaction Example provides an example of the use of JMS API local transactions.

Using the JMS API in Java EE Applications

This section describes the ways in which using the JMS API in enterprise bean applications or web applications differs from using it in application clients.

A general rule in the Java EE platform specification applies to all Java EE components that use the JMS API within EJB or web containers:

Application components in the web and EJB containers must not attempt to create more than one active (not closed) Session object per connection.

This rule does not apply to application clients. The application client container supports the creation of multiple sessions for each connection.

Using @Resource Annotations in Enterprise Bean or Web Components

When you use the @Resource annotation in an application client component, you normally declare the JMS resource static:

@Resource(lookup = "jms/ConnectionFactory")
private static ConnectionFactory connectionFactory;

@Resource(lookup = "jms/Queue")
private static Queue queue;

However, when you use this annotation in a session bean, a message-driven bean, or a web component, do not declare the resource static:

@Resource(lookup = "jms/ConnectionFactory")
private ConnectionFactory connectionFactory;

@Resource(lookup = "jms/Topic")
private Topic topic;

If you declare the resource static, runtime errors will result.

Using Session Beans to Produce and to Synchronously Receive Messages

An application that produces messages or synchronously receives them can use a session bean to perform these operations. The example in An Application That Uses the JMS API with a Session Bean uses a stateless session bean to publish messages to a topic.

Because a blocking synchronous receive ties up server resources, it is not a good programming practice to use such a receive call in an enterprise bean. Instead, use a timed synchronous receive, or use a message-driven bean to receive messages asynchronously. For details about blocking and timed synchronous receives, see Writing the Clients for the Synchronous Receive Example.

Using the JMS API in an enterprise bean or web application is in many ways similar to using it in an application client. The main differences are the areas of resource management and transactions.

Resource Management

The JMS API resources are a JMS API connection and a JMS API session. In general, it is important to release JMS resources when they are no longer being used. Here are some useful practices to follow.

Transactions

Instead of using local transactions, you use container-managed transactions for bean methods that perform sends or receives, allowing the EJB container to handle transaction demarcation. Because container-managed transactions are the default, you do not have to use an annotation to specify them.

You can use bean-managed transactions and the javax.transaction.UserTransaction interface’s transaction demarcation methods, but you should do so only if your application has special requirements and you are an expert in using transactions. Usually, container-managed transactions produce the most efficient and correct behavior. This tutorial does not provide any examples of bean-managed transactions.

Using Message-Driven Beans to Receive Messages Asynchronously

The sections What Is a Message-Driven Bean? and How Does the JMS API Work with the Java EE Platform? describe how the Java EE platform supports a special kind of enterprise bean, the message-driven bean, which allows Java EE applications to process JMS messages asynchronously. Session beans allow you to send messages and to receive them synchronously but not asynchronously.

A message-driven bean is a message listener that can reliably consume messages from a queue or a durable subscription. The messages can be sent by any Java EE component (from an application client, another enterprise bean, or a web component) or from an application or a system that does not use Java EE technology.

Like a message listener in an application client, a message-driven bean contains an onMessage method that is called automatically when a message arrives. Like a message listener, a message-driven bean class can implement helper methods invoked by the onMessage method to aid in message processing.

A message-driven bean, however, differs from an application client’s message listener in the following ways:

The EJB container automatically performs several setup tasks that a stand-alone client has to do:

If JMS is integrated with the application server using a resource adapter, the JMS resource adapter handles these tasks for the EJB container.

Your message-driven bean class must implement the javax.jms.MessageListener interface and the onMessage method.

It may implement a @PostConstruct callback method to create a connection, and a @PreDestroy callback method to close the connection. Typically, it implements these methods if it produces messages or does synchronous receives from another destination.

The bean class commonly injects a MessageDrivenContext resource, which provides some additional methods that you can use for transaction management.

The main difference between a message-driven bean and a session bean is that a message-driven bean has no local or remote interface. Instead, it has only a bean class.

A message-driven bean is similar in some ways to a stateless session bean: Its instances are relatively short-lived and retain no state for a specific client. The instance variables of the message-driven bean instance can contain some state across the handling of client messages: for example, a JMS API connection, an open database connection, or an object reference to an enterprise bean object.

Like a stateless session bean, a message-driven bean can have many interchangeable instances running at the same time. The container can pool these instances to allow streams of messages to be processed concurrently. The container attempts to deliver messages in chronological order when it does not impair the concurrency of message processing, but no guarantees are made as to the exact order in which messages are delivered to the instances of the message-driven bean class. Because concurrency can affect the order in which messages are delivered, you should write your applications to handle messages that arrive out of sequence.

For example, your application could manage conversations by using application-level sequence numbers. An application-level conversation control mechanism with a persistent conversation state could cache later messages until earlier messages have been processed.

Another way to ensure order is to have each message or message group in a conversation require a confirmation message that the sender blocks on receipt of. This forces the responsibility for order back on the sender and more tightly couples senders to the progress of message-driven beans.

To create a new instance of a message-driven bean, the container does the following:

To remove an instance of a message-driven bean, the container calls the @PreDestroy callback method.

Figure 30–9 shows the lifecycle of a message-driven bean.

Figure 30–9 Lifecycle of a Message-Driven Bean

Diagram showing message-driven bean lifecycle

Managing Distributed Transactions

JMS client applications use JMS API local transactions (described in Using JMS API Local Transactions), which allow the grouping of sends and receives within a specific JMS session. Java EE applications commonly use distributed transactions to ensure the integrity of accesses to external resources. For example, distributed transactions allow multiple applications to perform atomic updates on the same database, and they allow a single application to perform atomic updates on multiple databases.

In a Java EE application that uses the JMS API, you can use transactions to combine message sends or receives with database updates and other resource manager operations. You can access resources from multiple application components within a single transaction. For example, a servlet can start a transaction, access multiple databases, invoke an enterprise bean that sends a JMS message, invoke another enterprise bean that modifies an EIS system using the Connector architecture, and finally commit the transaction. Your application cannot, however, both send a JMS message and receive a reply to it within the same transaction; the restriction described in Using JMS API Local Transactions still applies.

Distributed transactions within the EJB container can be either of two kinds:

You can use either container-managed transactions or bean-managed transactions with message-driven beans. To ensure that all messages are received and handled within the context of a transaction, use container-managed transactions and use the Required transaction attribute (the default) for the onMessage method. This means that if there is no transaction in progress, a new transaction will be started before the method is called and will be committed when the method returns.

When you use container-managed transactions, you can call the following MessageDrivenContext methods:

If you use bean-managed transactions, the delivery of a message to the onMessage method takes place outside the distributed transaction context. The transaction begins when you call the UserTransaction.begin method within the onMessage method, and it ends when you call UserTransaction.commit or UserTransaction.rollback. Any call to the Connection.createSession method must take place within the transaction. If you call UserTransaction.rollback, the message is not redelivered, whereas calling setRollbackOnly for container-managed transactions does cause a message to be redelivered.

Neither the JMS API specification nor the Enterprise JavaBeans specification (available from http://jcp.org/en/jsr/detail?id=318) specifies how to handle calls to JMS API methods outside transaction boundaries. The Enterprise JavaBeans specification does state that the EJB container is responsible for acknowledging a message that is successfully processed by the onMessage method of a message-driven bean that uses bean-managed transactions. Using bean-managed transactions allows you to process the message by using more than one transaction or to have some parts of the message processing take place outside a transaction context. In most cases, however, container-managed transactions provide greater reliability and are therefore preferable.

When you create a session in an enterprise bean, the container ignores the arguments you specify, because it manages all transactional properties for enterprise beans. It is still a good idea to specify arguments of true and 0 to the createSession method to make this situation clear:

session = connection.createSession(true, 0);

When you use container-managed transactions, you normally use the Required transaction attribute (the default) for your enterprise bean’s business methods.

You do not specify a message acknowledgment mode when you create a message-driven bean that uses container-managed transactions. The container acknowledges the message automatically when it commits the transaction.

If a message-driven bean uses bean-managed transactions, the message receipt cannot be part of the bean-managed transaction, so the container acknowledges the message outside the transaction.

If the onMessage method throws a RuntimeException, the container does not acknowledge processing the message. In that case, the JMS provider will redeliver the unacknowledged message in the future.

Using the JMS API with Application Clients and Web Components

An application client in a Java EE application can use the JMS API in much the same way that a stand-alone client program does. It can produce messages, and it can consume messages by using either synchronous receives or message listeners. See Chapter 17, A Message-Driven Bean Example for an example of an application client that produces messages.

The Java EE platform specification does not impose strict constraints on how web components should use the JMS API. In the GlassFish Server, a web component can send messages and consume them synchronously but cannot consume them asynchronously.

Because a blocking synchronous receive ties up server resources, it is not a good programming practice to use such a receive call in a web component. Instead, use a timed synchronous receive. For details about blocking and timed synchronous receives, see Writing the Clients for the Synchronous Receive Example.

Further Information about JMS

For more information about JMS, see:

Chapter 31 Java Message Service Examples

This chapter provides examples that show how to use the JMS API in various kinds of Java EE applications. It covers the following topics:

The examples are in the following directory:

tut-install/examples/jms/

    To build and run the examples, you will do the following:

  1. Use NetBeans IDE or the Ant tool to compile and package the example.

  2. Use the Ant tool to create resources.

  3. Use NetBeans IDE or the Ant tool to deploy the example.

  4. Use NetBeans IDE, the appclient command, or the Ant tool to run the client.

Each example has a build.xml file that refers to files in the following directory:

tut-install/examples/bp-project/

See Chapter 17, A Message-Driven Bean Example for a simpler example of a Java EE application that uses the JMS API.

Writing Simple JMS Applications

This section shows how to create, package, and run simple JMS clients that are packaged as application clients and deployed to a Java EE server. The clients demonstrate the basic tasks that a JMS application must perform:

In a Java EE application, some of these tasks are performed, in whole or in part, by the container. If you learn about these tasks, you will have a good basis for understanding how a JMS application works on the Java EE platform.

Each example uses two clients: one that sends messages and one that receives them. You can run the clients in NetBeans IDE or in two terminal windows.

When you write a JMS client to run in a enterprise bean application, you use many of the same methods in much the same sequence as you do for an application client. However, there are some significant differences. Using the JMS API in Java EE Applications describes these differences, and this chapter provides examples that illustrate them.

The examples for this section are in the following directory:

tut-install/examples/jms/simple/

The examples are in the following four subdirectories:

producer

synchconsumer

asynchconsumer

messagebrowser

A Simple Example of Synchronous Message Receives

This section describes the sending and receiving clients in an example that uses the receive method to consume messages synchronously. This section then explains how to compile, package, and run the clients using the GlassFish Server.

The following sections describe the steps in creating and running the example.

Writing the Clients for the Synchronous Receive Example

    The sending client, producer/src/java/Producer.java, performs the following steps:

  1. Injects resources for a connection factory, queue, and topic:

    @Resource(lookup = "jms/ConnectionFactory")
    private static ConnectionFactory connectionFactory;
    @Resource(lookup = "jms/Queue")private static Queue queue;
    @Resource(lookup = "jms/Topic")private static Topic topic;
  2. Retrieves and verifies command-line arguments that specify the destination type and the number of arguments:

    final int NUM_MSGS;
    String destType = args[0];
    System.out.println("Destination type is " + destType);
    if ( ! ( destType.equals("queue") || destType.equals("topic") ) ) { 
        System.err.println("Argument must be \”queue\” or " + "\”topic\”");
        System.exit(1);
    }
    if (args.length == 2){ 
        NUM_MSGS = (new Integer(args[1])).intValue();
    } 
    else { 
        NUM_MSGS = 1;
    }
  3. Assigns either the queue or topic to a destination object, based on the specified destination type:

    Destination dest = null;
    try { 
        if (destType.equals("queue")) { 
            dest = (Destination) queue; 
        } else { 
            dest = (Destination) topic; 
        }
    } 
    catch (Exception e) {
        System.err.println("Error setting destination: " + e.toString()); 
        e.printStackTrace(); 
        System.exit(1);
    }
  4. Creates a Connection and a Session:

    Connection connection = connectionFactory.createConnection(); 
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
  5. Creates a MessageProducer and a TextMessage:

    MessageProducer producer = session.createProducer(dest);
    TextMessage message = session.createTextMessage();
  6. Sends one or more messages to the destination:

    for (int i = 0; i < NUM_MSGS; i++) { 
        message.setText("This is message " + (i + 1) + " from producer"); 
        System.out.println("Sending message: " + message.getText()); 
        producer.send(message);
    }
  7. Sends an empty control message to indicate the end of the message stream:

    producer.send(session.createMessage());

    Sending an empty message of no specified type is a convenient way to indicate to the consumer that the final message has arrived.

  8. Closes the connection in a finally block, automatically closing the session and MessageProducer:

    } finally { 
        if (connection != null) { 
            try { connection.close(); } 
            catch (JMSException e) { } 
        }
    }

    The receiving client, synchconsumer/src/java/SynchConsumer.java, performs the following steps:

  1. Injects resources for a connection factory, queue, and topic.

  2. Assigns either the queue or topic to a destination object, based on the specified destination type.

  3. Creates a Connection and a Session.

  4. Creates a MessageConsumer:

    consumer = session.createConsumer(dest);
  5. Starts the connection, causing message delivery to begin:

    connection.start();
  6. Receives the messages sent to the destination until the end-of-message-stream control message is received:

    while (true) {
        Message m = consumer.receive(1); 
        if (m != null) { 
            if (m instanceof TextMessage) { 
                message = (TextMessage) m; 
                System.out.println("Reading message: " + message.getText()); 
            } else { 
                break; 
            } 
        }
    }

    Because the control message is not a TextMessage, the receiving client terminates the while loop and stops receiving messages after the control message arrives.

  7. Closes the connection in a finally block, automatically closing the session and MessageConsumer.

The receive method can be used in several ways to perform a synchronous receive. If you specify no arguments or an argument of 0, the method blocks indefinitely until a message arrives:

Message m = consumer.receive();
Message m = consumer.receive(0);

For a simple client, this may not matter. But if you do not want your application to consume system resources unnecessarily, use a timed synchronous receive. Do one of the following:

The SynchConsumer client uses an indefinite while loop to receive messages, calling receive with a timeout argument. Calling receiveNoWait would have the same effect.

Starting the JMS Provider

When you use the GlassFish Server, your JMS provider is the GlassFish Server. Start the server as described in Starting and Stopping the GlassFish Server.

ProcedureTo Create JMS Administered Objects for the Synchronous Receive Example

Creating the JMS administered objects for this section involves the following:

If you built and ran the simplemessage example in Chapter 17, A Message-Driven Bean Example and did not delete the resources afterward, you need to create only the topic resource.

You can create these objects using the Ant tool. To create all the resources, follow these steps.

  1. In a terminal window, go to the producer directory:


    cd producer
    
  2. To create all the resources, type the following command:


    ant create-resources
    

    To create only the topic resource, type the following command:


    ant create-topic
    

    These Ant targets use the asadmin create-jms-resource command to create the connection factory and the destination resources.

  3. To verify that the resources have been created, use the following command:


    asadmin list-jms-resources
    

    The output looks like this:


    jms/Queue
    jms/Topic
    jms/ConnectionFactory
    Command list-jms-resources executed successfully.

Building, Packaging, Deploying, and Running the Clients for the Synchronous Receive Example

To run these examples using the GlassFish Server, package each one in an application client JAR file. The application client JAR file requires a manifest file, located in the src/conf directory for each example, along with the .class file.

The build.xml file for each example contains Ant targets that compile and package the example. The targets place the .class file for the example in the build/jar directory. Then the targets use the jar command to package the class file and the manifest file in an application client JAR file.

Because the examples use the common interfaces, you can run them using either a queue or a topic.

ProcedureTo Build and Package the Clients for the Synchronous Receive Example Using NetBeans IDE

  1. In NetBeans IDE, select File->Open Project.

  2. In the Open Project dialog, navigate to:


    tut-install/examples/jms/simple/
    
  3. Select the producer folder.

  4. Select the Open as Main Project check box.

  5. Click Open Project.

  6. In the Projects tab, right-click the project and select Build.

  7. Select the synchconsumer folder.

  8. Select the Open as Main Project check box.

  9. Click Open Project.

  10. In the Projects tab, right-click the project and select Build.

ProcedureTo Deploy and Run the Clients for the Synchronous Receive Example Using NetBeans IDE

  1. Deploy and run the Producer example:

    1. Right-click the producer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue 3
      
    4. Click OK.

    5. Right-click the project and select Run.

      The output of the program looks like this (along with some application client container output):


      Destination type is queue
      Sending message: This is message 1 from producer
      Sending message: This is message 2 from producer
      Sending message: This is message 3 from producer

      The messages are now in the queue, waiting to be received.


      Note –

      When you run an application client, there is usually a noticeable delay between the first two application client container messages and the remainder of the output.


  2. Now deploy and run the SynchConsumer example:

    1. Right-click the synchconsumer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue
      
    4. Click OK.

    5. Right-click the project and select Run.

      The output of the program looks like this (along with some application client container output):


      Destination type is queue
      Reading message: This is message 1 from producer
      Reading message: This is message 2 from producer
      Reading message: This is message 3 from producer
  3. Now try running the programs in the opposite order. Right-click the synchconsumer project and select Run.

    The Output pane displays the destination type and then appears to hang, waiting for messages.

  4. Right-click the producer project and select Run.

    The Output pane shows the output of both programs, in two different tabs.

  5. Now run the Producer example using a topic instead of a queue.

    1. Right-click the producer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      topic 3
      
    4. Click OK.

    5. Right-click the project and select Run.

      The output looks like this (along with some application client container output):


      Destination type is topic
      Sending message: This is message 1 from producer
      Sending message: This is message 2 from producer
      Sending message: This is message 3 from producer
  6. Now run the SynchConsumer example using the topic.

    1. Right-click the synchconsumer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      topic
      
    4. Click OK.

    5. Right-click the project and select Run.

      The result, however, is different. Because you are using a topic, messages that were sent before you started the consumer cannot be received. (See Publish/Subscribe Messaging Domain, for details.) Instead of receiving the messages, the program appears to hang.

  7. Run the Producer example again. Right-click the producer project and select Run.

    Now the SynchConsumer example receives the messages:


    Destination type is topic
    Reading message: This is message 1 from producer
    Reading message: This is message 2 from producer
    Reading message: This is message 3 from producer

ProcedureTo Build and Package the Clients for the Synchronous Receive Example Using Ant

  1. In a terminal window, go to the producer directory:


    cd producer
    
  2. Type the following command:


    ant
    
  3. In a terminal window, go to the synchconsumer directory:


    cd ../synchconsumer
    
  4. Type the following command:


    ant
    

    The targets place the application client JAR file in the dist directory for each example.

ProcedureTo Deploy and Run the Clients for the Synchronous Receive Example Using Ant and the appclient Command

You can run the clients using the appclient command. The build.xml file for each project includes a target that deploys the client and then retrieves the client stubs that the appclient command uses. Each of the clients takes one or more command-line arguments: a destination type and, for Producer, a number of messages.

To build, deploy, and run the Producer and SynchConsumer examples using Ant and the appclient command, follow these steps.

To run the clients, you need two terminal windows.

  1. In a terminal window, go to the producer directory:


    cd ../producer
    
  2. Deploy the client JAR file to the GlassFish Server, then retrieve the client stubs:


    ant getclient
    

    Ignore the message that states that the application is deployed at a URL.

  3. Run the Producer program, sending three messages to the queue:


    appclient -client client-jar/producerClient.jar queue 3
    

    The output of the program looks like this (along with some application client container output):


    Destination type is queue
    Sending message: This is message 1 from producer
    Sending message: This is message 2 from producer
    Sending message: This is message 3 from producer

    The messages are now in the queue, waiting to be received.


    Note –

    When you run an application client, there is usually a noticeable delay between the first two application client container messages and the remainder of the output.


  4. In the same window, go to the synchconsumer directory:


    cd ../synchconsumer
    
  5. Deploy the client JAR file to the GlassFish Server, then retrieve the client stubs:


    ant getclient
    

    Ignore the message that states that the application is deployed at a URL.

  6. Run the SynchConsumer client, specifying the queue:


    appclient -client client-jar/synchconsumerClient.jar queue
    

    The output of the client looks like this (along with some application client container output):


    Destination type is queue
    Reading message: This is message 1 from producer
    Reading message: This is message 2 from producer
    Reading message: This is message 3 from producer
  7. Now try running the clients in the opposite order. Run the SynchConsumer client:


    appclient -client client-jar/synchconsumerClient.jar queue
    

    The client displays the destination type and then appears to hang, waiting for messages.

  8. In a different terminal window, run the Producer client.


    cd tut-install/examples/jms/simple/producer
    appclient -client client-jar/producerClient.jar queue 3
    

    When the messages have been sent, the SynchConsumer client receives them and exits.

  9. Now run the Producer client using a topic instead of a queue:


    appclient -client client-jar/producerClient.jar topic 3
    

    The output of the client looks like this (along with some application client container output):


    Destination type is topic
    Sending message: This is message 1 from producer
    Sending message: This is message 2 from producer
    Sending message: This is message 3 from producer
  10. Now run the SynchConsumer client using the topic:


    appclient -client client-jar/synchconsumerClient.jar topic
    

    The result, however, is different. Because you are using a topic, messages that were sent before you started the consumer cannot be received. (See Publish/Subscribe Messaging Domain, for details.) Instead of receiving the messages, the client appears to hang.

  11. Run the Producer client again.

    Now the SynchConsumer client receives the messages (along with some application client container output):


    Destination type is topic
    Reading message: This is message 1 from producer
    Reading message: This is message 2 from producer
    Reading message: This is message 3 from producer

A Simple Example of Asynchronous Message Consumption

This section describes the receiving clients in an example that uses a message listener to consume messages asynchronously. This section then explains how to compile and run the clients using the GlassFish Server.

Writing the Clients for the Asynchronous Receive Example

The sending client is producer/src/java/Producer.java, the same client used in the example in A Simple Example of Synchronous Message Receives.

An asynchronous consumer normally runs indefinitely. This one runs until the user types the letter q or Q to stop the client.

    The receiving client, asynchconsumer/src/java/AsynchConsumer.java, performs the following steps:

  1. Injects resources for a connection factory, queue, and topic.

  2. Assigns either the queue or topic to a destination object, based on the specified destination type.

  3. Creates a Connection and a Session.

  4. Creates a MessageConsumer.

  5. Creates an instance of the TextListener class and registers it as the message listener for the MessageConsumer:

    listener = new TextListener();consumer.setMessageListener(listener);
  6. Starts the connection, causing message delivery to begin.

  7. Listens for the messages published to the destination, stopping when the user types the character q or Q:

    System.out.println("To end program, type Q or q, " + "then <return>");
    inputStreamReader = new InputStreamReader(System.in);
    while (!((answer == ’q’) || (answer == ’Q’))) { 
        try { 
            answer = (char) inputStreamReader.read(); 
        } catch (IOException e) { 
            System.out.println("I/O exception: " + e.toString()); 
        }
    }
  8. Closes the connection, which automatically closes the session and MessageConsumer.

    The message listener, asynchconsumer/src/java/TextListener.java, follows these steps:

  1. When a message arrives, the onMessage method is called automatically.

  2. The onMessage method converts the incoming message to a TextMessage and displays its content. If the message is not a text message, it reports this fact:

    public void onMessage(Message message) { 
        TextMessage msg = null; 
        try { 
            if (message instanceof TextMessage) { 
                msg = (TextMessage) message; 
                 System.out.println("Reading message: " + msg.getText()); 
            } else { 
                 System.out.println("Message is not a " + "TextMessage"); 
            } 
        } catch (JMSException e) { 
            System.out.println("JMSException in onMessage(): " + e.toString()); 
        } catch (Throwable t) { 
            System.out.println("Exception in onMessage():" + t.getMessage()); 
        }
    }

You will use the connection factory and destinations you created in To Create JMS Administered Objects for the Synchronous Receive Example.

ProcedureTo Build and Package the AsynchConsumer Client Using NetBeans IDE

  1. In NetBeans IDE, select File->Open Project.

  2. In the Open Project dialog, navigate to:


    tut-install/examples/jms/simple/
    
  3. Select the asynchconsumer folder.

  4. Select the Open as Main Project check box.

  5. Click Open Project.

  6. In the Projects tab, right-click the project and select Build.

ProcedureTo Deploy and Run the Clients for the Asynchronous Receive Example Using NetBeans IDE

  1. Run the AsynchConsumer example:

    1. Right-click the asynchconsumer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      topic
      
    4. Click OK.

    5. Right-click the project and select Run.

      The client displays the following lines and appears to hang:


      Destination type is topic
      To end program, type Q or q, then <return>
  2. Now run the Producer example:

    1. Right-click the producer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      topic 3
      
    4. Click OK.

    5. Right-click the project and select Run.

      The output of the client looks like this:


      Destination type is topic
      Sending message: This is message 1 from producer
      Sending message: This is message 2 from producer
      Sending message: This is message 3 from producer

      In the other window, the AsynchConsumer client displays the following:


      Destination type is topic
      To end program, type Q or q, then <return>
      Reading message: This is message 1 from producer
      Reading message: This is message 2 from producer
      Reading message: This is message 3 from producer
      Message is not a TextMessage

      The last line appears because the client has received the non-text control message sent by the Producer client.

  3. Type Q or q in the Output window and press Return to stop the client.

  4. Now run the Producer client using a queue.

    In this case, as with the synchronous example, you can run the Producer client first, because there is no timing dependency between the sender and receiver.

    1. Right-click the producer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue 3
      
    4. Click OK.

    5. Right-click the project and select Run.

      The output of the client looks like this:


      Destination type is queue
      Sending message: This is message 1 from producer
      Sending message: This is message 2 from producer
      Sending message: This is message 3 from producer
  5. Run the AsynchConsumer client.

    1. Right-click the asynchconsumer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue
      
    4. Click OK.

    5. Right-click the project and select Run.

      The output of the client looks like this:


      Destination type is queue
      To end program, type Q or q, then <return>
      Reading message: This is message 1 from producer
      Reading message: This is message 2 from producer
      Reading message: This is message 3 from producer
      Message is not a TextMessage
  6. Type Q or q in the Output window and press Return to stop the client.

ProcedureTo Build and Package the AsynchConsumer Client Using Ant

  1. In a terminal window, go to the asynchconsumer directory:


    cd ../asynchconsumer
    
  2. Type the following command:


    ant
    

    The targets package both the main class and the message listener class in the JAR file and place the file in the dist directory for the example.

ProcedureTo Deploy and Run the Clients for the Asynchronous Receive Example Using Ant and the appclient Command

  1. Deploy the client JAR file to the GlassFish Server, then retrieve the client stubs:


    ant getclient
    

    Ignore the message that states that the application is deployed at a URL.

  2. Run the AsynchConsumer client, specifying the topic destination type.


    appclient -client client-jar/asynchconsumerClient.jar topic
    

    The client displays the following lines (along with some application client container output) and appears to hang:


    Destination type is topic
    To end program, type Q or q, then <return>
  3. In the terminal window where you ran the Producer client previously, run the client again, sending three messages.


    appclient -client client-jar/producerClient.jar topic 3
    

    The output of the client looks like this (along with some application client container output):


    Destination type is topic
    Sending message: This is message 1 from producer
    Sending message: This is message 2 from producer
    Sending message: This is message 3 from producer

    In the other window, the AsynchConsumer client displays the following (along with some application client container output):


    Destination type is topic
    To end program, type Q or q, then <return>
    Reading message: This is message 1 from producer
    Reading message: This is message 2 from producer
    Reading message: This is message 3 from producer
    Message is not a TextMessage

    The last line appears because the client has received the non-text control message sent by the Producer client.

  4. Type Q or q and press Return to stop the client.

  5. Now run the clients using a queue.

    In this case, as with the synchronous example, you can run the Producer client first, because there is no timing dependency between the sender and receiver:


    appclient -client client-jar/producerClient.jar queue 3
    

    The output of the client looks like this:


    Destination type is queue
    Sending message: This is message 1 from producer
    Sending message: This is message 2 from producer
    Sending message: This is message 3 from producer
  6. Run the AsynchConsumer client:


    appclient -client client-jar/asynchconsumerClient.jar queue
    

    The output of the client looks like this (along with some application client container output):


    Destination type is queue
    To end program, type Q or q, then <return>
    Reading message: This is message 1 from producer
    Reading message: This is message 2 from producer
    Reading message: This is message 3 from producer
    Message is not a TextMessage
  7. Type Q or q to stop the client.

A Simple Example of Browsing Messages in a Queue

This section describes an example that creates a QueueBrowser object to examine messages on a queue, as described in JMS Queue Browsers. This section then explains how to compile, package, and run the example using the GlassFish Server.

Writing the Client for the Queue Browser Example

To create a QueueBrowser for a queue, you call the Session.createBrowser method with the queue as the argument. You obtain the messages in the queue as an Enumeration object. You can then iterate through the Enumeration object and display the contents of each message.

    The messagebrowser/src/java/MessageBrowser.java client performs the following steps:

  1. Injects resources for a connection factory and a queue.

  2. Creates a Connection and a Session.

  3. Creates a QueueBrowser:

    QueueBrowser browser = session.createBrowser(queue);
  4. Retrieves the Enumeration that contains the messages:

    Enumeration msgs = browser.getEnumeration();
  5. Verifies that the Enumeration contains messages, then displays the contents of the messages:

    if ( !msgs.hasMoreElements() ) { 
        System.out.println("No messages in queue");
    } else { 
        while (msgs.hasMoreElements()) { 
            Message tempMsg = (Message)msgs.nextElement(); 
            System.out.println("Message: " + tempMsg); 
        }
    }
  6. Closes the connection, which automatically closes the session and QueueBrowser.

The format in which the message contents appear is implementation-specific. In the GlassFish Server, the message format looks like this:


Message contents: 
Text:   This is message 3 from producer
Class:                  com.sun.messaging.jmq.jmsclient.TextMessageImpl
getJMSMessageID():      ID:14-129.148.71.199(f9:86:a2:d5:46:9b)-40814-1255980521747
getJMSTimestamp():      1129061034355
getJMSCorrelationID():  null
JMSReplyTo:             null
JMSDestination:         PhysicalQueue
getJMSDeliveryMode():   PERSISTENT
getJMSRedelivered():    false
getJMSType():           null
getJMSExpiration():     0
getJMSPriority():       4
Properties:             null

You will use the connection factory and queue you created in To Create JMS Administered Objects for the Synchronous Receive Example.

ProcedureTo Build, Package, Deploy, and Run the MessageBrowser Client Using NetBeans IDE

To build, package, deploy, and run the MessageBrowser example using NetBeans IDE, follow these steps.

You also need the Producer example to send the message to the queue, and one of the consumer clients to consume the messages after you inspect them. If you did not do so already, package these examples.

  1. In NetBeans IDE, select File->Open Project.

  2. In the Open Project dialog, navigate to:


    tut-install/examples/jms/simple/
    
  3. Select the messagebrowser folder.

  4. Select the Open as Main Project check box.

  5. Click Open Project.

  6. In the Projects tab, right-click the project and select Build.

  7. Run the Producer client, sending one message to the queue:

    1. Right-click the producer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue
      
    4. Click OK.

    5. Right-click the project and select Run.

      The output of the client looks like this:


      Destination type is queue
      Sending message: This is message 1 from producer
  8. Run the MessageBrowser client. Right-click the messagebrowser project and select Run.

    The output of the client looks like this:


    Message: 
    Text: This is message 1 from producer
    Class: com.sun.messaging.jmq.jmsclient.TextMessageImpl
    getJMSMessageID(): ID:12-129.148.71.199(8c:34:4a:1a:1b:b8)-40883-1255980521747
    getJMSTimestamp(): 1129062957611
    getJMSCorrelationID(): null
    JMSReplyTo: null
    JMSDestination: PhysicalQueue
    getJMSDeliveryMode(): PERSISTENT
    getJMSRedelivered(): false
    getJMSType(): null
    getJMSExpiration(): 0
    getJMSPriority(): 4
    Properties: null
    Message: 
    Class: com.sun.messaging.jmq.jmsclient.MessageImpl
    getJMSMessageID(): ID:13-129.148.71.199(8c:34:4a:1a:1b:b8)-40883-1255980521747
    getJMSTimestamp(): 1129062957616
    getJMSCorrelationID(): null
    JMSReplyTo: null
    JMSDestination: PhysicalQueue
    getJMSDeliveryMode(): PERSISTENT
    getJMSRedelivered(): false
    getJMSType(): null
    getJMSExpiration(): 0
    getJMSPriority(): 4
    Properties: null

    The first message is the TextMessage, and the second is the non-text control message.

  9. Run the SynchConsumer client to consume the messages.

    1. Right-click the synchconsumer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue
      
    4. Click OK.

    5. Right-click the project and select Run.

      The output of the client looks like this:


      Destination type is queue
      Reading message: This is message 1 from producer

ProcedureTo Build, Package, Deploy, and Run the MessageBrowser Client Using Ant and the appclient Command

To build, package, deploy, and run the MessageBrowser example using Ant, follow these steps.

You also need the Producer example to send the message to the queue, and one of the consumer clients to consume the messages after you inspect them. If you did not do so already, package these examples.

To run the clients, you need two terminal windows.

  1. In a terminal window, go to the messagebrowser directory.


    cd ../messagebrowser
    
  2. Type the following command:


    ant
    

    The targets place the application client JAR file in the dist directory for the example.

  3. Go to the producer directory.

  4. Run the Producer client, sending one message to the queue:


    appclient -client client-jar/producerClient.jar queue
    

    The output of the client looks like this (along with some application client container output):


    Destination type is queue
    Sending message: This is message 1 from producer
  5. Go to the messagebrowser directory.

  6. Deploy the client JAR file to the GlassFish Server, then retrieve the client stubs:


    ant getclient
    

    Ignore the message that states that the application is deployed at a URL.

  7. Because this example takes no command-line arguments, you can run the MessageBrowser client using the following command:


    ant run
    

    Alternatively, you can type the following command:


    appclient -client client-jar/messagebrowserClient.jar
    

    The output of the client looks like this (along with some application client container output):


    Message: 
    Text: This is message 1 from producer
    Class: com.sun.messaging.jmq.jmsclient.TextMessageImpl
    getJMSMessageID(): ID:12-129.148.71.199(8c:34:4a:1a:1b:b8)-40883-1255980521747
    getJMSTimestamp(): 1255980521747
    getJMSCorrelationID(): null
    JMSReplyTo: null
    JMSDestination: PhysicalQueue
    getJMSDeliveryMode(): PERSISTENT
    getJMSRedelivered(): false
    getJMSType(): null
    getJMSExpiration(): 0
    getJMSPriority(): 4
    Properties: null
    Message: 
    Class: com.sun.messaging.jmq.jmsclient.MessageImpl
    getJMSMessageID(): ID:13-129.148.71.199(8c:34:4a:1a:1b:b8)-40883-1255980521767
    getJMSTimestamp(): 1255980521767
    getJMSCorrelationID(): null
    JMSReplyTo: null
    JMSDestination: PhysicalQueue
    getJMSDeliveryMode(): PERSISTENT
    getJMSRedelivered(): false
    getJMSType(): null
    getJMSExpiration(): 0
    getJMSPriority(): 4
    Properties: null

    The first message is the TextMessage, and the second is the non-text control message.

  8. Go to the synchconsumer directory.

  9. Run the SynchConsumer client to consume the messages:


    appclient -client client-jar/synchconsumerClient.jar queue
    

    The output of the client looks like this (along with some application client container output):


    Destination type is queue
    Reading message: This is message 1 from producer

Running JMS Clients on Multiple Systems

JMS clients that use the GlassFish Server can exchange messages with each other when they are running on different systems in a network. The systems must be visible to each other by name (the UNIX host name or the Microsoft Windows computer name) and must both be running the GlassFish Server.


Note –

Any mechanism for exchanging messages between systems is specific to the Java EE server implementation. This tutorial describes how to use the GlassFish Server for this purpose.


    Suppose that you want to run the Producer client on one system, earth, and the SynchConsumer client on another system, jupiter. Before you can do so, you need to perform these tasks:

  1. Create two new connection factories

  2. Change the name of the default JMS host on one system

  3. Edit the source code for the two examples

  4. Recompile and repackage the examples


    Note –

    A limitation in the JMS provider in the GlassFish Server may cause a runtime failure to create a connection to systems that use the Dynamic Host Configuration Protocol (DHCP) to obtain an IP address. You can, however, create a connection from a system that uses DHCP to a system that does not use DHCP. In the examples in this tutorial, earth can be a system that uses DHCP, and jupiter can be a system that does not use DHCP.


When you run the clients, they will work as shown in Figure 31–1. The client run on earth needs the queue on earth only in order that the resource injection will succeed. The connection, session, and message producer are all created on jupiter using the connection factory that points to jupiter. The messages sent from earth will be received on jupiter.

Figure 31–1 Sending Messages from One System to Another

Diagram showing a message being sent to a queue by a
producer on Earth and being received by a consumer on Jupiter

For examples showing how to deploy more complex applications on two different systems, see An Application Example That Consumes Messages from a Remote Server and An Application Example That Deploys a Message-Driven Bean on Two Servers.

ProcedureTo Create Administered Objects for Multiple Systems

To run these clients, you must do the following:

You do not have to install the tutorial examples on both systems, but you must be able to access the filesystem where it is installed. You may find it more convenient to install the tutorial examples on both systems if the two systems use different operating systems (for example, Windows and Solaris). Otherwise you will have to edit the file tut-install/examples/bp-project/build.properties and change the location of the javaee.home property each time you build or run a client on a different system.

  1. Start the GlassFish Server on earth.

  2. Start the GlassFish Server on jupiter.

  3. To create a new connection factory on jupiter, follow these steps:

    1. From a command shell on jupiter, go to the directory tut-install/examples/jms/simple/producer/.

    2. Type the following command:


      ant create-local-factory
      

      The create-local-factory target, defined in the build.xml file for the Producer example, creates a connection factory named jms/JupiterConnectionFactory.

  4. To create a new connection factory on earth that points to the connection factory on jupiter, follow these steps:

    1. From a command shell on earth, go to the directory tut-install/examples/jms/simple/producer/.

    2. Type the following command:


      ant create-remote-factory -Dsys=remote-system-name
      

      Replace remote-system-name with the actual name of the remote system.

      The create-remote-factory target, defined in the build.xml file for the Producer example, also creates a connection factory named jms/JupiterConnectionFactory. In addition, it sets the AddressList property for this factory to the name of the remote system.

  5. If you have already been working on either earth or jupiter, you have the queue and topic on one system. On the system that does not have the queue and topic, type the following command:


    ant create-resources
    

Changing the Default Host Name

By default, the default host name for the JMS service on the GlassFish Server is localhost. To access the JMS service from another system, however, you must change the host name. You can change it to either the actual host name or to 0.0.0.0.

You can change the default host name using either the Administration Console or the asadmin command.

ProcedureTo Change the Default Host Name Using the Administration Console

  1. On jupiter, start the Administration Console by opening a browser at http://localhost:4848/.

  2. In the navigation tree, expand the Configuration node.

  3. Under the Configuration node, expand the Java Message Service node.

  4. Under the Java Message Service node, expand the JMS Hosts node.

  5. Under the JMS Hosts node, select default_JMS_host.

    The Edit JMS Host page opens.

  6. In the Host field, type the name of the system, or type 0.0.0.0.

  7. Click Save.

  8. Restart the GlassFish Server.

ProcedureTo Change the Default Host Name Using the asadmin Command

  1. Specify a command like one of the following:


    asadmin set server-config.jms-service.jms-host.default_JMS_host.host="0.0.0.0"
    

    asadmin set server-config.jms-service.jms-host.default_JMS_host.host="hostname"
    
  2. Restart the GlassFish Server.

ProcedureTo Edit, Build, Package, Deploy, and Run the Clients Using NetBeans IDE

These steps assume that you have the tutorial installed on both of the two systems you are using and that you are able to access the file system of jupiter from earth or vice versa. You will edit the source files to specify the new connection factory. Then you will rebuild and run the clients. Follow these steps.

  1. To edit the source files, follow these steps:

    1. On earth,, open the following file in NetBeans IDE:

      tut-install/examples/jms/simple/producer/src/java/Producer.java
      
    2. Find the following line:

      @Resource(lookup = "jms/ConnectionFactory")
    3. Change the line to the following:

      @Resource(lookup = "jms/JupiterConnectionFactory")
    4. On jupiter, open the following file inNetBeans IDE:

      tut-install/examples/jms/simple/synchconsumer/src/java/SynchConsumer.java
      
    5. Repeat Step b and Step c.

  2. To recompile and repackage the Producer example on earth, right-click the producer project and select Clean and Build.

  3. To recompile and repackage the SynchConsumer example on jupiter, right-click the synchconsumer project and select Clean and Build.

  4. On earth, deploy and run Producer. Follow these steps:

    1. Right-click the producer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue 3
      
    4. Click OK.

    5. Right-click the project and select Run.

  5. On jupiter, run SynchConsumer. Follow these steps:

    1. Right-click the synchconsumer project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type the following:


      queue
      
    4. Click OK.

    5. Right-click the project and select Run.

ProcedureTo Edit, Build, Package, Deploy, and Run the Clients Using Ant and the appclient Command

These steps assume that you have the tutorial installed on both of the two systems you are using and that you are able to access the file system of jupiter from earth or vice versa. You will edit the source files to specify the new connection factory. Then you will rebuild and run the clients.

  1. To edit the source files, follow these steps:

    1. On earth,, open the following file in a text editor:

      tut-install/examples/jms/simple/producer/src/java/Producer.java
      
    2. Find the following line:

      @Resource(lookup = "jms/ConnectionFactory")
    3. Change the line to the following:

      @Resource(lookup = "jms/JupiterConnectionFactory")
    4. On jupiter, open the following file in a text editor:

      tut-install/examples/jms/simple/synchconsumer/src/java/SynchConsumer.java
      
    5. Repeat Step b and Step c.

  2. To recompile and repackage the Producer example on earth, type the following:


    ant
    
  3. To recompile and repackage the SynchConsumer example on jupiter, go to the synchconsumer directory and type the following:


    ant
    
  4. On earth, deploy and run Producer. Follow these steps:

    1. On earth, from the producer directory, deploy the client JAR file to the GlassFish Server, then retrieve the client stubs:


      ant getclient
      

      Ignore the message that states that the application is deployed at a URL.

    2. To run the client, type the following:


      appclient -client client-jar/producerClient.jar queue 3
      
  5. On jupiter, run SynchConsumer. Follow these steps:

    1. From the synchconsumer directory, deploy the client JAR file to the GlassFish Server, then retrieve the client stubs:


      ant getclient
      

      Ignore the message that states that the application is deployed at a URL.

    2. To run the client, type the following:


      appclient -client client-jar/synchconsumerClient.jar queue
      

Undeploying and Cleaning the Simple JMS Examples

After you finish running the examples, you can undeploy them and remove the build artifacts.

You can also use Ant targets in the producer/build.xml file to delete the destinations and connection factories you created in To Create JMS Administered Objects for the Synchronous Receive Example. However, it is recommended that you keep them, because they will be used in most of the examples later in this chapter. After you have created them, they will be available whenever you restart the GlassFish Server.

If you wish to delete the connection factories and destinations, go to the producer directory and type the following:


ant delete-resources
ant delete-remote-factory

Remember to delete the remote connection factory on both systems.

Writing Robust JMS Applications

The following examples show how to use some of the more advanced features of the JMS API.

A Message Acknowledgment Example

The AckEquivExample.java client shows how both of the following two scenarios ensure that a message will not be acknowledged until processing of it is complete:

With a message listener, the automatic acknowledgment happens when the onMessage method returns (that is, after message processing has finished). With a synchronous receiver, the client acknowledges the message after processing is complete. If you use AUTO_ACKNOWLEDGE with a synchronous receive, the acknowledgment happens immediately after the receive call; if any subsequent processing steps fail, the message cannot be redelivered.

The example is in the following directory:

tut-install/examples/jms/advanced/ackequivexample/src/java/

The example contains an AsynchSubscriber class with a TextListener class, a MultiplePublisher class, a SynchReceiver class, a SynchSender class, a main method, and a method that runs the other classes’ threads.

The example uses the following objects:

To create the new queue and connection factory, you can use Ant targets defined in the file tut-install/examples/jms/advanced/ackequivexample/build.xml.

ProcedureTo Build, Package, Deploy, and Run the ackequivexample Using NetBeans IDE

  1. In a terminal window, go to the following directory:

    tut-install/examples/jms/advanced/ackequivexample/
    
  2. To create the objects needed in this example, type the following commands:


    ant create-control-queue
    ant create-durable-cf
    
  3. To build and package the client, follow these steps.

    1. In NetBeans IDE, select File->Open Project.

    2. In the Open Project dialog, navigate to:


      tut-install/examples/jms/advanced/
      
    3. Select the ackequivexample folder.

    4. Select the Open as Main Project check box.

    5. Click Open Project.

    6. In the Projects tab, right-click the project and select Build.

  4. To run the client, right-click the ackequivexample project and select Run.

    The client output looks something like this (along with some application client container output):


    Queue name is jms/ControlQueue
    Queue name is jms/Queue
    Topic name is jms/Topic
    Connection factory name is jms/DurableConnectionFactory
      SENDER: Created client-acknowledge session
      SENDER: Sending message: Here is a client-acknowledge message
      RECEIVER: Created client-acknowledge session
      RECEIVER: Processing message: Here is a client-acknowledge message
      RECEIVER: Now I’ll acknowledge the message
    SUBSCRIBER: Created auto-acknowledge session
    SUBSCRIBER: Sending synchronize message to control queue
    PUBLISHER: Created auto-acknowledge session
    PUBLISHER: Receiving synchronize messages from control queue; count = 1
    PUBLISHER: Received synchronize message;  expect 0 more
    PUBLISHER: Publishing message: Here is an auto-acknowledge message 1
    PUBLISHER: Publishing message: Here is an auto-acknowledge message 2
    SUBSCRIBER: Processing message: Here is an auto-acknowledge message 1
    PUBLISHER: Publishing message: Here is an auto-acknowledge message 3
    SUBSCRIBER: Processing message: Here is an auto-acknowledge message 2
    SUBSCRIBER: Processing message: Here is an auto-acknowledge message 3
  5. After you run the client, you can delete the destination resource jms/ControlQueue. Go to the directory tut-install/examples/jms/advanced/ackequivexample/ and type the following command:


    ant delete-control-queue
    

    You will need the other resources for other examples.

ProcedureTo Build, Package, Deploy, and Run ackequivexample Using Ant

  1. In a terminal window, go to the following directory:

    tut-install/examples/jms/advanced/ackequivexample/
    
  2. To create the objects needed in this example, type the following commands:


    ant create-control-queue
    ant create-durable-cf
    
  3. To compile and package the client, type the following command:


    ant
    
  4. To deploy the client JAR file to the GlassFish Server, then retrieve the client stubs, type the following command:


    ant getclient
    

    Ignore the message that states that the application is deployed at a URL.

  5. Because this example takes no command-line arguments, you can run the client using the following command:


    ant run
    

    Alternatively, you can type the following command:


    appclient -client client-jar/ackequivexampleClient.jar
    

    The client output looks something like this (along with some application client container output):


    Queue name is jms/ControlQueue
    Queue name is jms/Queue
    Topic name is jms/Topic
    Connection factory name is jms/DurableConnectionFactory
      SENDER: Created client-acknowledge session
      SENDER: Sending message: Here is a client-acknowledge message
      RECEIVER: Created client-acknowledge session
      RECEIVER: Processing message: Here is a client-acknowledge message
      RECEIVER: Now I’ll acknowledge the message
    SUBSCRIBER: Created auto-acknowledge session
    SUBSCRIBER: Sending synchronize message to control queue
    PUBLISHER: Created auto-acknowledge session
    PUBLISHER: Receiving synchronize messages from control queue; count = 1
    PUBLISHER: Received synchronize message;  expect 0 more
    PUBLISHER: Publishing message: Here is an auto-acknowledge message 1
    PUBLISHER: Publishing message: Here is an auto-acknowledge message 2
    SUBSCRIBER: Processing message: Here is an auto-acknowledge message 1
    PUBLISHER: Publishing message: Here is an auto-acknowledge message 3
    SUBSCRIBER: Processing message: Here is an auto-acknowledge message 2
    SUBSCRIBER: Processing message: Here is an auto-acknowledge message 3
  6. After you run the client, you can delete the destination resource jms/ControlQueue. Go to the directory tut-install/examples/jms/advanced/ackequivexample/ and type the following command:


    ant delete-control-queue
    

    You will need the other resources for other examples.

A Durable Subscription Example

The DurableSubscriberExample.java example shows how durable subscriptions work. It demonstrates that a durable subscription is active even when the subscriber is not active. The example contains a DurableSubscriber class, a MultiplePublisher class, a main method, and a method that instantiates the classes and calls their methods in sequence.

The example is in the following directory:

tut-install/examples/jms/advanced/durablesubscriberexample/src/java/

The example begins in the same way as any publish/subscribe client: The subscriber starts, the publisher publishes some messages, and the subscriber receives them. At this point, the subscriber closes itself. The publisher then publishes some messages while the subscriber is not active. The subscriber then restarts and receives the messages.

ProcedureTo Build, Package, Deploy, and Run durablesubscriberexample Using NetBeans IDE

  1. In a terminal window, go to the following directory:

    tut-install/examples/jms/advanced/durablesubscriberexample/
    
  2. If you did not do so for A Message Acknowledgment Example, create a connection factory named jms/DurableConnectionFactory:


    ant create-durable-cf
    
  3. To compile and package the client, follow these steps:

    1. In NetBeans IDE, select File->Open Project.

    2. In the Open Project dialog, navigate to:


      tut-install/examples/jms/advanced/
      
    3. Select the durablesubscriberexample folder.

    4. Select the Open as Main Project check box.

    5. Click Open Project.

    6. In the Projects tab, right-click the project and select Build.

  4. To run the client, right-click the durablesubscriberexample project and select Run.

    The output looks something like this (along with some application client container output):


    Connection factory without client ID is jms/ConnectionFactory
    Connection factory with client ID is jms/DurableConnectionFactory
    Topic name is jms/Topic
    Starting subscriber
    PUBLISHER: Publishing message: Here is a message 1
    SUBSCRIBER: Reading message: Here is a message 1
    PUBLISHER: Publishing message: Here is a message 2
    SUBSCRIBER: Reading message: Here is a message 2
    PUBLISHER: Publishing message: Here is a message 3
    SUBSCRIBER: Reading message: Here is a message 3
    Closing subscriber
    PUBLISHER: Publishing message: Here is a message 4
    PUBLISHER: Publishing message: Here is a message 5
    PUBLISHER: Publishing message: Here is a message 6
    Starting subscriber
    SUBSCRIBER: Reading message: Here is a message 4
    SUBSCRIBER: Reading message: Here is a message 5
    SUBSCRIBER: Reading message: Here is a message 6
    Closing subscriber
    Unsubscribing from durable subscription
  5. After you run the client, you can delete the connection factory jms/DurableConnectionFactory. Go to the directory tut-install/examples/jms/advanced/durablesubscriberexample/ and type the following command:


    ant delete-durable-cf
    

ProcedureTo Build, Package, Deploy, and Run durablesubscriberexample Using Ant

  1. In a terminal window, go to the following directory:

    tut-install/examples/jms/advanced/durablesubscriberexample/
    
  2. If you did not do so for A Message Acknowledgment Example, create a connection factory named jms/DurableConnectionFactory:


    ant create-durable-cf
    
  3. To compile and package the client, type the following command:


    ant
    
  4. To deploy the client JAR file to the GlassFish Server, then retrieve the client stubs, type the following command:


    ant getclient
    

    Ignore the message that states that the application is deployed at a URL.

  5. Because this example takes no command-line arguments, you can run the client using the following command:


    ant run
    

    Alternatively, you can type the following command:


    appclient -client client-jar/durablesubscriberexampleClient.jar
    
  6. After you run the client, you can delete the connection factory jms/DurableConnectionFactory. Go to the directory tut-install/examples/jms/advanced/durablesubscriberexample/ and type the following command:


    ant delete-durable-cf
    

A Local Transaction Example

The TransactedExample.java example demonstrates the use of transactions in a JMS client application. The example is in the following directory:

tut-install/examples/jms/advanced/transactedexample/src/java/

    This example shows how to use a queue and a topic in a single transaction as well as how to pass a session to a message listener’s constructor function. The example represents a highly simplified e-commerce application in which the following things happen.

  1. A retailer sends a MapMessage to the vendor order queue, ordering a quantity of computers, and waits for the vendor’s reply:

    producer = session.createProducer(vendorOrderQueue);
    outMessage = session.createMapMessage();
    outMessage.setString("Item", "Computer(s)");
    outMessage.setInt("Quantity", quantity);
    outMessage.setJMSReplyTo(retailerConfirmQueue);
    producer.send(outMessage);
    System.out.println("Retailer: ordered " + quantity + " computer(s)");
    orderConfirmReceiver = session.createConsumer(retailerConfirmQueue);
    connection.start();
  2. The vendor receives the retailer’s order message and sends an order message to the supplier order topic in one transaction. This JMS transaction uses a single session, so you can combine a receive from a queue with a send to a topic. Here is the code that uses the same session to create a consumer for a queue and a producer for a topic:

    vendorOrderReceiver = session.createConsumer(vendorOrderQueue);
    supplierOrderProducer = session.createProducer(supplierOrderTopic);

    The following code receives the incoming message, sends an outgoing message, and commits the session. The message processing has been removed to keep the sequence simple:

    inMessage = vendorOrderReceiver.receive();
    // Process the incoming message and format the outgoing 
    // message
    ...
    supplierOrderProducer.send(orderMessage);
    ...
    session.commit();
  3. Each supplier receives the order from the order topic, checks its inventory, and then sends the items ordered to the queue named in the order message’s JMSReplyTo field. If it does not have enough in stock, the supplier sends what it has. The synchronous receive from the topic and the send to the queue take place in one JMS transaction.

    receiver = session.createConsumer(orderTopic);
    ...
    inMessage = receiver.receive();
    if (inMessage instanceof MapMessage) {
        orderMessage = (MapMessage) inMessage;
    }
    // Process message
    MessageProducer producer = 
        session.createProducer((Queue) orderMessage.getJMSReplyTo());
    outMessage = session.createMapMessage();
    // Add content to message
    producer.send(outMessage);
    // Display message contentssession.commit();
  4. The vendor receives the replies from the suppliers from its confirmation queue and updates the state of the order. Messages are processed by an asynchronous message listener; this step shows the use of JMS transactions with a message listener.

    MapMessage component = (MapMessage) message;
    ...
    orderNumber = component.getInt("VendorOrderNumber");
    Order order = Order.getOrder(orderNumber).processSubOrder(component);
    session.commit();
  5. When all outstanding replies are processed for a given order, the vendor message listener sends a message notifying the retailer whether it can fulfill the order.

    Queue replyQueue = (Queue) order.order.getJMSReplyTo();
    MessageProducer producer = session.createProducer(replyQueue);
    MapMessage retailerConfirmMessage = session.createMapMessage();
    // Format the message
    producer.send(retailerConfirmMessage);
    session.commit();
  6. The retailer receives the message from the vendor:

    inMessage = (MapMessage) orderConfirmReceiver.receive();

Figure 31–2 illustrates these steps.

Figure 31–2 Transactions: JMS Client Example

Diagram of steps in transaction example

The example contains five classes: GenericSupplier, Order, Retailer, Vendor, and VendorMessageListener. The example also contains a main method and a method that runs the threads of the Retailer, Vendor, and two supplier classes.

All the messages use the MapMessage message type. Synchronous receives are used for all message reception except for the case of the vendor processing the replies of the suppliers. These replies are processed asynchronously and demonstrate how to use transactions within a message listener.

At random intervals, the Vendor class throws an exception to simulate a database problem and cause a rollback.

All classes except Retailer use transacted sessions.

The example uses three queues named jms/AQueue, jms/BQueue, and jms/CQueue, and one topic named jms/OTopic.

ProcedureTo Build, Package, Deploy, and Run transactedexample Using NetBeans IDE

  1. In a terminal window, go to the following directory:

    tut-install/examples/jms/advanced/transactedexample/
    
  2. Create the necessary resources using the following command:


    ant create-resources
    

    This command creates three destination resources with the names jms/AQueue, jms/BQueue, and jms/CQueue, all of type javax.jms.Queue, and one destination resource with the name jms/OTopic, of type javax.jms.Topic.

  3. To compile and package the client, follow these steps:

    1. In NetBeans IDE, select File->Open Project.

    2. In the Open Project dialog, navigate to:


      tut-install/examples/jms/advanced/
      
    3. Select the transactedexample folder.

    4. Select the Open as Main Project check box.

    5. Click Open Project.

    6. In the Projects tab, right-click the project and select Build.

  4. To deploy and run the client, follow these steps:

    1. Right-click the transactedexample project and select Properties.

    2. Select Run from the Categories tree.

    3. In the Arguments field, type a number that specifies the number of computers to order:

      3

    4. Click OK.

    5. Right-click the project and select Run.

    The output looks something like this (along with some application client container output):


    Quantity to be ordered is 3
    Retailer: ordered 3 computer(s)
    Vendor: Retailer ordered 3 Computer(s)
    Vendor: ordered 3 monitor(s) and hard drive(s)
    Monitor Supplier: Vendor ordered 3 Monitor(s)
    Monitor Supplier: sent 3 Monitor(s)
      Monitor Supplier: committed transaction
      Vendor: committed transaction 1
    Hard Drive Supplier: Vendor ordered 3 Hard Drive(s)
    Hard Drive Supplier: sent 1 Hard Drive(s)
    Vendor: Completed processing for order 1
      Hard Drive Supplier: committed transaction
    Vendor: unable to send 3 computer(s)
      Vendor: committed transaction 2
    Retailer: Order not filled
    Retailer: placing another order
    Retailer: ordered 6 computer(s)
    Vendor: JMSException occurred: javax.jms.JMSException: 
    Simulated database concurrent access exception
    javax.jms.JMSException: Simulated database concurrent access exception
            at TransactedExample$Vendor.run(Unknown Source)
      Vendor: rolled back transaction 1
    Vendor: Retailer ordered 6 Computer(s)
    Vendor: ordered 6 monitor(s) and hard drive(s)
    Monitor Supplier: Vendor ordered 6 Monitor(s)
    Hard Drive Supplier: Vendor ordered 6 Hard Drive(s)
    Monitor Supplier: sent 6 Monitor(s)
      Monitor Supplier: committed transaction
    Hard Drive Supplier: sent 6 Hard Drive(s)
      Hard Drive Supplier: committed transaction
      Vendor: committed transaction 1
    Vendor: Completed processing for order 2
    Vendor: sent 6 computer(s)
    Retailer: Order filled
      Vendor: committed transaction 2
  5. After you run the client, you can delete the physical destinations and the destination resources. Go to the directory tut-install/examples/jms/advanced/transactedexample/ and type the following command:


    ant delete-resources
    

ProcedureTo Build, Package, Deploy, and Run transactedexample Using Ant and the appclient Command

  1. In a terminal window, go to the following directory:

    tut-install/examples/jms/advanced/transactedexample/
    
  2. Create the necessary resources using the following command:


    ant create-resources
    

    This command creates three destination resources with the names jms/AQueue, jms/BQueue, and jms/CQueue, all of type javax.jms.Queue, and one destination resource with the name jms/OTopic, of type javax.jms.Topic.

  3. To build and package the client, type the following command:


    ant
    
  4. Deploy the client JAR file to the GlassFish Server, then retrieve the client stubs:


    ant getclient
    

    Ignore the message that states that the application is deployed at a URL.

  5. Use a command like the following to run the client.

    The argument specifies the number of computers to order.


    appclient -client client-jar/transactedexampleClient.jar 3
    

    The output looks something like this (along with some application client container output):


    Quantity to be ordered is 3
    Retailer: ordered 3 computer(s)
    Vendor: Retailer ordered 3 Computer(s)
    Vendor: ordered 3 monitor(s) and hard drive(s)
    Monitor Supplier: Vendor ordered 3 Monitor(s)
    Monitor Supplier: sent 3 Monitor(s)
      Monitor Supplier: committed transaction
      Vendor: committed transaction 1
    Hard Drive Supplier: Vendor ordered 3 Hard Drive(s)
    Hard Drive Supplier: sent 1 Hard Drive(s)
    Vendor: Completed processing for order 1
      Hard Drive Supplier: committed transaction
    Vendor: unable to send 3 computer(s)
      Vendor: committed transaction 2
    Retailer: Order not filled
    Retailer: placing another order
    Retailer: ordered 6 computer(s)
    Vendor: JMSException occurred: javax.jms.JMSException: 
    Simulated database concurrent access exception
    javax.jms.JMSException: Simulated database concurrent access exception
            at TransactedExample$Vendor.run(Unknown Source)
      Vendor: rolled back transaction 1
    Vendor: Retailer ordered 6 Computer(s)
    Vendor: ordered 6 monitor(s) and hard drive(s)
    Monitor Supplier: Vendor ordered 6 Monitor(s)
    Hard Drive Supplier: Vendor ordered 6 Hard Drive(s)
    Monitor Supplier: sent 6 Monitor(s)
      Monitor Supplier: committed transaction
    Hard Drive Supplier: sent 6 Hard Drive(s)
      Hard Drive Supplier: committed transaction
      Vendor: committed transaction 1
    Vendor: Completed processing for order 2
    Vendor: sent 6 computer(s)
    Retailer: Order filled
      Vendor: committed transaction 2
  6. After you run the client, you can delete the physical destinations and the destination resources. Go to the directory tut-install/examples/jms/advanced/transactedexample/ and type the following command:


    ant delete-resources
    

An Application That Uses the JMS API with a Session Bean

This section explains how to write, compile, package, deploy, and run an application that uses the JMS API in conjunction with a session bean. The application contains the following components:

You will find the source files for this section in the directory tut-install/examples/jms/clientsessionmdb/. Path names in this section are relative to this directory.

Writing the Application Components for the clientsessionmdb Example

This application demonstrates how to send messages from an enterprise bean (in this case, a session bean) rather than from an application client, as in the example in Chapter 17, A Message-Driven Bean Example. Figure 31–3 illustrates the structure of this application.

Figure 31–3 An Enterprise Bean Application: Client to Session Bean to Message-Driven Bean

Diagram of application showing an application client
calling a session bean, which publishes a message that is consumed by a message-driven
bean

The Publisher enterprise bean in this example is the enterprise-application equivalent of a wire-service news feed that categorizes news events into six news categories. The message-driven bean could represent a newsroom, where the sports desk, for example, would set up a subscription for all news events pertaining to sports.

The application client in the example injects the Publisher enterprise bean’s remote home interface and then calls the bean’s business method. The enterprise bean creates 18 text messages. For each message, it sets a String property randomly to one of six values representing the news categories and then publishes the message to a topic. The message-driven bean uses a message selector for the property to limit which of the published messages it receives.

Coding the Application Client: MyAppClient.java

The application client, clientsessionmdb-app-client/src/java/MyAppClient.java, performs no JMS API operations and so is simpler than the client in Chapter 17, A Message-Driven Bean Example. The client uses dependency injection to obtain the Publisher enterprise bean’s business interface:

@EJB(name="PublisherRemote")
static private PublisherRemote publisher;

The client then calls the bean’s business method twice.

Coding the Publisher Session Bean

The Publisher bean is a stateless session bean that has one business method. The Publisher bean uses a remote interface rather than a local interface because it is accessed from the application client.

The remote interface, clientsessionmdb-ejb/src/java/sb/PublisherRemote.java, declares a single business method, publishNews.

The bean class, clientsessionmdb-ejb/src/java/sb/PublisherBean.java, implements the publishNews method and its helper method chooseType. The bean class also injects SessionContext, ConnectionFactory, and Topic resources and implements @PostConstruct and @PreDestroy callback methods. The bean class begins as follows:

@Stateless
@Remote({PublisherRemote.class})
public class PublisherBean implements PublisherRemote {

    @Resource
    private SessionContext sc;

    @Resource(lookup = "jms/ConnectionFactory")
    private ConnectionFactory connectionFactory;

    @Resource(lookup = "jms/Topic")
    private Topic topic;
    ...

The @PostConstruct callback method of the bean class, makeConnection, creates the Connection used by the bean. The business method publishNews creates a Session and a MessageProducer and publishes the messages.

The @PreDestroy callback method, endConnection, deallocates the resources that were allocated by the @PostConstruct callback method. In this case, the method closes the Connection.

Coding the Message-Driven Bean: MessageBean.java

The message-driven bean class, clientsessionmdb-ejb/src/java/mdb/MessageBean.java, is almost identical to the one in Chapter 17, A Message-Driven Bean Example. However, the @MessageDriven annotation is different, because instead of a queue the bean is using a topic with a durable subscription, and it is also using a message selector. Therefore, the annotation sets the activation config properties messageSelector, subscriptionDurability, clientId, and subscriptionName, as follows:

@MessageDriven(mappedName = "jms/Topic", activationConfig =  {
    @ActivationConfigProperty(propertyName = "messageSelector",
            propertyValue = "NewsType = 'Sports' OR NewsType = 'Opinion'")
    , @ActivationConfigProperty(propertyName = "subscriptionDurability",
            propertyValue = "Durable")
    , @ActivationConfigProperty(propertyName = "clientId",
            propertyValue = "MyID")
    , @ActivationConfigProperty(propertyName = "subscriptionName",
            propertyValue = "MySub")
    })

Note –

For a message-driven bean, the destination is specified with the mappedName element instead of the lookup element.


The JMS resource adapter uses these properties to create a connection factory for the message-driven bean that allows the bean to use a durable subscriber.

Creating Resources for the clientsessionmdb Example

This example uses the topic named jms/Topic and the connection factory jms/ConnectionFactory, which you created in To Create JMS Administered Objects for the Synchronous Receive Example. If you deleted the connection factory or topic, you can create them again using targets in the build.xml file for this example. Use the following commands to create the resources:


ant create-cf
ant create-topic

ProcedureTo Build, Package, Deploy, and Run the clientsessionmdb Example Using NetBeans IDE

  1. To compile and package the project, follow these steps:

    1. In NetBeans IDE, select File->Open Project.

    2. In the Open Project dialog, navigate to:


      tut-install/examples/jms/
      
    3. Select the clientsessionmdb folder.

    4. Select the Open as Main Project check box and the Open Required Projects check box.

    5. Click Open Project.

    6. In the Projects tab, right-click the clientsessionmdb project and select Build.

      This task creates the following:

      • An application client JAR file that contains the client class file and the session bean’s remote interface, along with a manifest file that specifies the main class and places the EJB JAR file in its classpath

      • An EJB JAR file that contains both the session bean and the message-driven bean

      • An application EAR file that contains the two JAR files

  2. Right-click the project and select Run.

    This command deploys the project, returns a JAR file named clientsessionmdbClient.jar, and then executes it.

    The output of the application client in the Output pane looks like this (preceded by application client container output):


    To view the bean output,
     check <install_dir>/domains/domain1/logs/server.log.

    The output from the enterprise beans appears in the server log (domain-dir/logs/server.log), wrapped in logging information. The Publisher session bean sends two sets of 18 messages numbered 0 through 17. Because of the message selector, the message-driven bean receives only the messages whose NewsType property is Sports or Opinion.

ProcedureTo Build, Package, Deploy, and Run the clientsessionmdb Example Using Ant

  1. Go to the following directory:

    tut-install/examples/jms/clientsessionmdb/
    
  2. To compile the source files and package the application, use the following command:


    ant
    

    The ant command creates the following:

    • An application client JAR file that contains the client class file and the session bean’s remote interface, along with a manifest file that specifies the main class and places the EJB JAR file in its classpath

    • An EJB JAR file that contains both the session bean and the message-driven bean

    • An application EAR file that contains the two JAR files

    The clientsessionmdb.ear file is created in the dist directory.

  3. To deploy the application and run the client, use the following command:


    ant run
    

    Ignore the message that states that the application is deployed at a URL.

    The client displays these lines (preceded by application client container output):

    To view the bean output,
     check <install_dir>/domains/domain1/logs/server.log.

    The output from the enterprise beans appears in the server log file, wrapped in logging information. The Publisher session bean sends two sets of 18 messages numbered 0 through 17. Because of the message selector, the message-driven bean receives only the messages whose NewsType property is Sports or Opinion.

An Application That Uses the JMS API with an Entity

This section explains how to write, compile, package, deploy, and run an application that uses the JMS API with an entity. The application uses the following components:

You will find the source files for this section in the directory tut-install/examples/jms/clientmdbentity/. Path names in this section are relative to this directory.

Overview of the clientmdbentity Example Application

This application simulates, in a simplified way, the work flow of a company’s human resources (HR) department when it processes a new hire. This application also demonstrates how to use the Java EE platform to accomplish a task that many JMS applications need to perform.

A JMS client must often wait for several messages from various sources. It then uses the information in all these messages to assemble a message that it then sends to another destination. The common term for this process is joining messages. Such a task must be transactional, with all the receives and the send as a single transaction. If not all the messages are received successfully, the transaction can be rolled back. For an application client example that illustrates this task, see A Local Transaction Example.

A message-driven bean can process only one message at a time in a transaction. To provide the ability to join messages, an application can have the message-driven bean store the interim information in an entity. The entity can then determine whether all the information has been received; when it has, the entity can report this back to one of the message-driven beans, which then creates and sends the message to the other destination. After it has completed its task, the entity can be removed.

The basic steps of the application are as follows.

  1. The HR department’s application client generates an employee ID for each new hire and then publishes a message (M1) containing the new hire’s name, employee ID, and position. The client then creates a temporary queue, ReplyQueue, with a message listener that waits for a reply to the message. (See Creating Temporary Destinations for more information.)

  2. Two message-driven beans process each message: One bean, OfficeMDB, assigns the new hire’s office number, and the other bean, EquipmentMDB, assigns the new hire’s equipment. The first bean to process the message creates and persists an entity named SetupOffice, then calls a business method of the entity to store the information it has generated. The second bean locates the existing entity and calls another business method to add its information.

  3. When both the office and the equipment have been assigned, the entity business method returns a value of true to the message-driven bean that called the method. The message-driven bean then sends to the reply queue a message (M2) describing the assignments. Then it removes the entity. The application client’s message listener retrieves the information.

Figure 31–4 illustrates the structure of this application. Of course, an actual HR application would have more components; other beans could set up payroll and benefits records, schedule orientation, and so on.

Figure 31–4 assumes that OfficeMDB is the first message-driven bean to consume the message from the client. OfficeMDB then creates and persists the SetupOffice entity and stores the office information. EquipmentMDB then finds the entity, stores the equipment information, and learns that the entity has completed its work. EquipmentMDB then sends the message to the reply queue and removes the entity.

Figure 31–4 An Enterprise Bean Application: Client to Message-Driven Beans to Entity

Diagram of application showing an application client,
two message-driven beans, and an entity

Writing the Application Components for the clientmdbentity Example

Writing the components of the application involves coding the application client, the message-driven beans, and the entity class.

Coding the Application Client: HumanResourceClient.java

    The application client, clientmdbentity-app-client/src/java/HumanResourceClient.java, performs the following steps:

  1. Injects ConnectionFactory and Topic resources

  2. Creates a TemporaryQueue to receive notification of processing that occurs, based on new-hire events it has published

  3. Creates a MessageConsumer for the TemporaryQueue, sets the MessageConsumer’s message listener, and starts the connection

  4. Creates a MessageProducer and a MapMessage

  5. Creates five new employees with randomly generated names, positions, and ID numbers (in sequence) and publishes five messages containing this information

The message listener, HRListener, waits for messages that contain the assigned office and equipment for each employee. When a message arrives, the message listener displays the information received and determines whether all five messages have arrived. When they have, the message listener notifies the main method, which then exits.

Coding the Message-Driven Beans for the clientmdbentity Example

This example uses two message-driven beans:

    The beans take the following steps:

  1. They inject MessageDrivenContext and ConnectionFactory resources.

  2. The onMessage method retrieves the information in the message. The EquipmentMDB’s onMessage method chooses equipment, based on the new hire’s position; the OfficeMDB’s onMessage method randomly generates an office number.

  3. After a slight delay to simulate real world processing hitches, the onMessage method calls a helper method, compose.

  4. The compose method takes the following steps:

    1. It either creates and persists the SetupOffice entity or finds it by primary key.

    2. It uses the entity to store the equipment or the office information in the database, calling either the doEquipmentList or the doOfficeNumber business method.

    3. If the business method returns true, meaning that all of the information has been stored, it creates a connection and a session, retrieves the reply destination information from the message, creates a MessageProducer, and sends a reply message that contains the information stored in the entity.

    4. It removes the entity.

Coding the Entity Class for the clientmdbentity Example

The SetupOffice class, clientmdbentity-ejb/src/java/eb/SetupOffice.java, is an entity class. The entity and the message-driven beans are packaged together in an EJB JAR file. The entity class is declared as follows:

@Entity
public class SetupOffice implements Serializable {

The class contains a no-argument constructor and a constructor that takes two arguments, the employee ID and name. It also contains getter and setter methods for the employee ID, name, office number, and equipment list. The getter method for the employee ID has the @Id annotation to indicate that this field is the primary key:

@Id public String getEmployeeId() {
    return id;
}

The class also implements the two business methods, doEquipmentList and doOfficeNumber, and their helper method, checkIfSetupComplete.

The message-driven beans call the business methods and the getter methods.

The persistence.xml file for the entity specifies the most basic settings:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 
             xmlns="http://java.sun.com/xml/ns/persistence" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
               http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="clientmdbentity-ejbPU" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/__default</jta-data-source>
    <class>eb.SetupOffice</class>
    <properties>
      <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

Creating Resources for the clientmdbentity Example

This example uses the connection factory jms/ConnectionFactory and the topic jms/Topic, both of which you used in An Application That Uses the JMS API with a Session Bean. It also uses the JDBC resource named jdbc/__default, which is enabled by default when you start the GlassFish Server.

If you deleted the connection factory or topic, you can create them again using targets in the build.xml file for this example. Use the following commands to create the resources:


ant create-cf
ant create-topic

ProcedureTo Build, Package, Deploy, and Run the clientmdbentity Example Using NetBeans IDE

  1. In NetBeans IDE, select File->Open Project.

  2. In the Open Project dialog, navigate to:


    tut-install/examples/jms/
    
  3. Select the clientmdbentity folder.

  4. Select the Open as Main Project check box and the Open Required Projects check box.

  5. Click Open Project.

  6. In the Projects tab, right-click the clientmdbentity project and select Build.

    This task creates the following:

    • An application client JAR file that contains the client class and listener class files, along with a manifest file that specifies the main class

    • An EJB JAR file that contains the message-driven beans and the entity class, along with the persistence.xml file

    • An application EAR file that contains the two JAR files along with an application.xml file

  7. If the Java DB database is not already running, follow these steps:

    1. Click the Services tab.

    2. Expand the Databases node.

    3. Right-click the Java DB node and select Start Server.

  8. In the Projects tab, right-click the project and select Run.

    This command deploys the project, returns a client JAR file named clientmdbentityClient.jar and then executes it.

    The output of the application client in the Output pane looks something like this:


    PUBLISHER: Setting hire ID to 50, name Bill Tudor, position Programmer
    PUBLISHER: Setting hire ID to 51, name Carol Jones, position Senior Programmer
    PUBLISHER: Setting hire ID to 52, name Mark Wilson, position Manager
    PUBLISHER: Setting hire ID to 53, name Polly Wren, position Senior Programmer
    PUBLISHER: Setting hire ID to 54, name Joe Lawrence, position Director
    Waiting for 5 message(s)
    New hire event processed:
      Employee ID: 52
      Name: Mark Wilson
      Equipment: PDA
      Office number: 294
    Waiting for 4 message(s)
    New hire event processed:
      Employee ID: 53
      Name: Polly Wren
      Equipment: Laptop
      Office number: 186
    Waiting for 3 message(s)
    New hire event processed:
      Employee ID: 54
      Name: Joe Lawrence
      Equipment: Java Phone
      Office number: 135
    Waiting for 2 message(s)
    New hire event processed:
      Employee ID: 50
      Name: Bill Tudor
      Equipment: Desktop System
      Office number: 200
    Waiting for 1 message(s)
    New hire event processed:
      Employee ID: 51
      Name: Carol Jones
      Equipment: Laptop
      Office number: 262

    The output from the message-driven beans and the entity class appears in the server log, wrapped in logging information.

    For each employee, the application first creates the entity and then finds it. You may see runtime errors in the server log, and transaction rollbacks may occur. The errors occur if both of the message-driven beans discover at the same time that the entity does not yet exist, so they both try to create it. The first attempt succeeds, but the second fails because the bean already exists. After the rollback, the second message-driven bean tries again and succeeds in finding the entity. Container-managed transactions allow the application to run correctly, in spite of these errors, with no special programming.

    You can run the application client repeatedly.

ProcedureTo Build, Package, Deploy, and Run the clientmdbentity Example Using Ant

  1. Go to the following directory:

    tut-install/examples/jms/clientmdbentity/
    
  2. To compile the source files and package the application, use the following command:


    ant
    

    The ant command creates the following:

    • An application client JAR file that contains the client class and listener class files, along with a manifest file that specifies the main class

    • An EJB JAR file that contains the message-driven beans and the entity class, along with the persistence.xml file

    • An application EAR file that contains the two JAR files along with an application.xml file

  3. To deploy the application and run the client, use the following command:


    ant run
    

    This command starts the database server if it is not already running, then deploys and runs the application.

    Ignore the message that states that the application is deployed at a URL.

    The output in the terminal window looks something like this (preceded by application client container output):


    running application client container.
    PUBLISHER: Setting hire ID to 50, name Bill Tudor, position Programmer
    PUBLISHER: Setting hire ID to 51, name Carol Jones, position Senior Programmer
    PUBLISHER: Setting hire ID to 52, name Mark Wilson, position Manager
    PUBLISHER: Setting hire ID to 53, name Polly Wren, position Senior Programmer
    PUBLISHER: Setting hire ID to 54, name Joe Lawrence, position Director
    Waiting for 5 message(s)
    New hire event processed:
      Employee ID: 52
      Name: Mark Wilson
      Equipment: PDA
      Office number: 294
    Waiting for 4 message(s)
    New hire event processed:
      Employee ID: 53
      Name: Polly Wren
      Equipment: Laptop
      Office number: 186
    Waiting for 3 message(s)
    New hire event processed:
      Employee ID: 54
      Name: Joe Lawrence
      Equipment: Java Phone
      Office number: 135
    Waiting for 2 message(s)
    New hire event processed:
      Employee ID: 50
      Name: Bill Tudor
      Equipment: Desktop System
      Office number: 200
    Waiting for 1 message(s)
    New hire event processed:
      Employee ID: 51
      Name: Carol Jones
      Equipment: Laptop
      Office number: 262

    The output from the message-driven beans and the entity class appears in the server log, wrapped in logging information.

    For each employee, the application first creates the entity and then finds it. You may see runtime errors in the server log, and transaction rollbacks may occur. The errors occur if both of the message-driven beans discover at the same time that the entity does not yet exist, so they both try to create it. The first attempt succeeds, but the second fails because the bean already exists. After the rollback, the second message-driven bean tries again and succeeds in finding the entity. Container-managed transactions allow the application to run correctly, in spite of these errors, with no special programming.

    You can run the application client repeatedly.

An Application Example That Consumes Messages from a Remote Server

This section and the following section explain how to write, compile, package, deploy, and run a pair of Java EE modules that run on two Java EE servers and that use the JMS API to interchange messages with each other. It is a common practice to deploy different components of an enterprise application on different systems within a company, and these examples illustrate on a small scale how to do this for an application that uses the JMS API.

However, the two examples work in slightly different ways. In this first example, the deployment information for a message-driven bean specifies the remote server from which it will consume messages. In the next example, the same message-driven bean is deployed on two different servers, so it is the client module that specifies the servers (one local, one remote) to which it is sending messages.

This first example divides the example in Chapter 17, A Message-Driven Bean Example into two modules: one containing the application client, and the other containing the message-driven bean.

You will find the source files for this section in tut-install/examples/jms/consumeremote/. Path names in this section are relative to this directory.

Overview of the consumeremote Example Modules

Except for the fact that it is packaged as two separate modules, this example is very similar to the one in Chapter 17, A Message-Driven Bean Example:

    The basic steps of the modules are as follows.

  1. The administrator starts two Java EE servers, one on each system.

  2. On the local server, the administrator deploys the message-driven bean module, which specifies the remote server where the client is deployed.

  3. On the remote server, the administrator places the client JAR file.

  4. The client module sends three messages to a queue.

  5. The message-driven bean consumes the messages.

Figure 31–5 illustrates the structure of this application. You can see that it is almost identical to Figure 17–1 except that there are two Java EE servers. The queue used is the one on the remote server; the queue must also exist on the local server for resource injection to succeed.

Figure 31–5 A Java EE Application That Consumes Messages from a Remote Server

Diagram of application showing a message-driven bean
that consumes messages from an application client on a remote server

Writing the Module Components for the consumeremote Example

Writing the components of the modules involves

The application client, jupiterclient/src/java/SimpleClient.java, is almost identical to the one in The simplemessage Application Client.

Similarly, the message-driven bean, earthmdb/src/java/MessageBean.java, is almost identical to the one in The Message-Driven Bean Class. The only significant difference is that the activation config properties include one property that specifies the name of the remote system. You need to edit the source file to specify the name of your system.

Creating Resources for the consumeremote Example

The application client can use any connection factory that exists on the remote server; it uses jms/ConnectionFactory. Both components use the queue named jms/Queue, which you created in To Create JMS Administered Objects for the Synchronous Receive Example. The message-driven bean does not need a previously created connection factory; the resource adapter creates one for it.

Using Two Application Servers for the consumeremote Example

As in Running JMS Clients on Multiple Systems, the two servers are named earth and jupiter.

The GlassFish Server must be running on both systems.

Before you can run the example, you must change the default name of the JMS host on jupiter, as described in To Change the Default Host Name Using the Administration Console. If you have already performed this task, you do not have to repeat it.

Which system you use to package and deploy the modules and which system you use to run the client depend on your network configuration (which file system you can access remotely). These instructions assume that you can access the file system of jupiter from earth but cannot access the file system of earth from jupiter. (You can use the same systems for jupiter and earth that you used in Running JMS Clients on Multiple Systems.)

You can package both modules on earth and deploy the message-driven bean there. The only action you perform on jupiter is running the client module.

ProcedureTo Build, Package, Deploy, and Run the consumeremoteModules Using NetBeans IDE

To edit the message-driven bean source file and then package, deploy, and run the modules using NetBeans IDE, follow these steps.

  1. In NetBeans IDE, select File->Open Project.

  2. In the Open Project dialog, navigate to:


    tut-install/examples/jms/consumeremote/
    
  3. Select the earthmdb folder.

  4. Select the Open as Main Project check box.

  5. Click Open Project.

  6. Edit the MessageBean.java file as follows:

    1. In the Projects tab, expand the earthmdb, Source Packages, and mdb nodes, then double-click MessageBean.java.

    2. Find the following line within the @MessageBean annotation:

              @ActivationConfigProperty(propertyName = "addressList",
                  propertyValue = "remotesystem"),
    3. Replace remotesystem with the name of your remote system.

  7. Right-click the earthmdb project and select Build.

    This command creates a JAR file that contains the bean class file.

  8. Select File->Open Project.

  9. Select the jupiterclient folder.

  10. Select the Open as Main Project check box.

  11. Click Open Project.

  12. In the Projects tab, right-click the jupiterclient project and select Build.

    This target creates a JAR file that contains the client class file and a manifest file.

  13. Right-click the earthmdb project and select Deploy.

  14. To copy the jupiterclient module to the remote system, follow these steps:

    1. Change to the directory jupiterclient/dist:


      cd ../jupiterclient/dist
      
    2. Type a command like the following:


      cp jupiterclient.jar F:/
      

      That is, copy the client JAR file to a location on the remote filesystem. You can use the file system user interface on your system instead of the command line.

  15. To run the application client, follow these steps:

    1. Go to the directory on the remote system (jupiter) where you copied the client JAR file.

    2. To deploy the client module and retrieve the client stubs, use the following command:


      asadmin deploy --retrieve . jupiterclient.jar
      

      This command deploys the client JAR file and retrieves the client stubs in a file named jupiterclientClient.jar

    3. To run the client, use the following command:


      appclient -client jupiterclientClient.jar
      

      On jupiter, the output of the appclient command looks like this (preceded by application client container output):


      Sending message: This is message 1 from jupiter
      Sending message: This is message 2 from jupiter
      Sending message: This is message 3 from jupiter

      On earth, the output in the server log looks something like this (preceded by logging information):


      MESSAGE BEAN: Message received: This is message 1 from jupiter
      MESSAGE BEAN: Message received: This is message 2 from jupiter
      MESSAGE BEAN: Message received: This is message 3 from jupiter

ProcedureTo Build, Package, Deploy, and Run the consumeremote Modules Using Ant

To edit the message-driven bean source file and then package, deploy, and run the modules using Ant, follow these steps.

  1. Open the file tut-install/examples/jms/consumeremote/earthmdb/src/java/mdb/MessageBean.java in an editor.

  2. Find the following line within the @MessageBean annotation:

            @ActivationConfigProperty(propertyName = "addressList",
                propertyValue = "remotesystem"),
  3. Replace remotesystem with the name of your remote system, then save and close the file.

  4. Go to the following directory:

    tut-install/examples/jms/consumeremote/earthmdb/
    
  5. Type the following command:


    ant
    

    This command creates a JAR file that contains the bean class file.

  6. Type the following command:


    ant deploy
    
  7. Go to the jupiterclient directory:


    cd ../jupiterclient
    
  8. Type the following command:


    ant
    

    This target creates a JAR file that contains the client class file and a manifest file.

  9. To copy the jupiterclient module to the remote system, follow these steps:

    1. Change to the directory jupiterclient/dist:


      cd ../jupiterclient/dist
      
    2. Type a command like the following:


      cp jupiterclient.jar F:/
      

      That is, copy the client JAR file to a location on the remote filesystem.

  10. To run the application client, follow these steps:

    1. Go to the directory on the remote system (jupiter) where you copied the client JAR file.

    2. To deploy the client module and retrieve the client stubs, use the following command:


      asadmin deploy --retrieve . jupiterclient.jar
      

      This command deploys the client JAR file and retrieves the client stubs in a file named jupiterclientClient.jar

    3. To run the client, use the following command:


      appclient -client jupiterclientClient.jar
      

      On jupiter, the output of the appclient command looks like this (preceded by application client container output):


      Sending message: This is message 1 from jupiter
      Sending message: This is message 2 from jupiter
      Sending message: This is message 3 from jupiter

      On earth, the output in the server log looks something like this (preceded by logging information):


      MESSAGE BEAN: Message received: This is message 1 from jupiter
      MESSAGE BEAN: Message received: This is message 2 from jupiter
      MESSAGE BEAN: Message received: This is message 3 from jupiter

An Application Example That Deploys a Message-Driven Bean on Two Servers

This section, like the preceding one, explains how to write, compile, package, deploy, and run a pair of Java EE modules that use the JMS API and run on two Java EE servers. The modules are slightly more complex than the ones in the first example.

The modules use the following components:

In this section, the term local server means the server on which both the application client and the message-driven bean are deployed (earth in the preceding example). The term remote server means the server on which only the message-driven bean is deployed (jupiter in the preceding example).

You will find the source files for this section in tut-install/examples/jms/sendremote/. Path names in this section are relative to this directory.

Overview of the sendremote Example Modules

This pair of modules is somewhat similar to the modules in An Application Example That Consumes Messages from a Remote Server in that the only components are a client and a message-driven bean. However, the modules here use these components in more complex ways. One module consists of the application client. The other module contains only the message-driven bean and is deployed twice, once on each server.

The basic steps of the modules are as follows.

  1. You start two Java EE servers, one on each system.

  2. On the local server (earth), you create two connection factories: one local and one that communicates with the remote server (jupiter). On the remote server, you create a connection factory that has the same name as the one that communicates with the remote server.

  3. The application client looks up the two connection factories (the local one and the one that communicates with the remote server) to create two connections, sessions, publishers, and subscribers. The subscribers use a message listener.

  4. Each publisher publishes five messages.

  5. Each of the local and the remote message-driven beans receives five messages and sends replies.

  6. The client’s message listener consumes the replies.

Figure 31–6 illustrates the structure of this application. M1 represents the first message sent using the local connection factory, and RM1 represents the first reply message sent by the local MDB. M2 represents the first message sent using the remote connection factory, and RM2 represents the first reply message sent by the remote MDB.

Figure 31–6 A Java EE Application That Sends Messages to Two Servers

Diagram of application showing an application client
sending messages to two servers and receiving the replies

Writing the Module Components for the sendremote Example

Writing the components of the modules involves coding the application client and the message-driven bean.

Coding the Application Client: MultiAppServerClient.java

    The application client class, multiclient/src/java/MultiAppServerClient.java, does the following.

  1. It injects resources for two connection factories and a topic.

  2. For each connection factory, it creates a connection, a publisher session, a publisher, a subscriber session, a subscriber, and a temporary topic for replies.

  3. Each subscriber sets its message listener, ReplyListener, and starts the connection.

  4. Each publisher publishes five messages and creates a list of the messages the listener should expect.

  5. When each reply arrives, the message listener displays its contents and removes it from the list of expected messages.

  6. When all the messages have arrived, the client exits.

Coding the Message-Driven Bean: ReplyMsgBean.java

    The message-driven bean class, replybean/src/ReplyMsgBean.java, does the following:

  1. Uses the @MessageDriven annotation:

    @MessageDriven(mappedName = "jms/Topic")
  2. Injects resources for the MessageDrivenContext and for a connection factory. It does not need a destination resource because it uses the value of the incoming message’s JMSReplyTo header as the destination.

  3. Uses a @PostConstruct callback method to create the connection, and a @PreDestroy callback method to close the connection.

    The onMessage method of the message-driven bean class does the following:

  1. Casts the incoming message to a TextMessage and displays the text

  2. Creates a connection, a session, and a publisher for the reply message

  3. Publishes the message to the reply topic

  4. Closes the connection

On both servers, the bean will consume messages from the topic jms/Topic.

Creating Resources for the sendremote Example

This example uses the connection factory named jms/ConnectionFactory and the topic named jms/Topic. These objects must exist on both the local and the remote servers.

This example uses an additional connection factory, jms/JupiterConnectionFactory, which communicates with the remote system; you created it in To Create Administered Objects for Multiple Systems. This connection factory must exist on the local server.

The build.xml file for the multiclient module contains targets that you can use to create these resources if you deleted them previously.

ProcedureTo Use Two Application Servers for the sendremote Example

If you are using NetBeans IDE, you need to add the remote server in order to deploy the message-driven bean there. To do so, follow these steps.

  1. In NetBeans IDE, click the Runtime tab.

  2. Right-click the Servers node and select Add Server. In the Add Server Instance dialog, follow these steps:

    1. Select GlassFish v3 (the default) from the Server list.

    2. In the Name field, specify a name slightly different from that of the local server, such as GlassFish v3 (2).

    3. Click Next.

    4. For the Server Location, you can either browse to the location of the GlassFish Server on the remote system or, if that location is not visible from the local system, use the default location on the local system.

    5. Click Next.

    6. Select the Register Remote Domain radio button.

    7. In the Host Name field, type the name of the remote system.

    8. Click Finish.

Next Steps

Before you can run the example, you must change the default name of the JMS host on jupiter, as described in To Change the Default Host Name Using the Administration Console. If you have already performed this task, you do not have to repeat it.

ProcedureTo Build, Package, Deploy, and Run the sendremote Modules Using NetBeans IDE

  1. To build the replybean module, follow these steps:

    1. In NetBeans IDE, select File->Open Project.

    2. In the Open Project dialog, navigate to:


      tut-install/examples/jms/sendremote/
      
    3. Select the replybean folder.

    4. Select the Open as Main Project check box.

    5. Click Open Project.

    6. In the Projects tab, right-click the replybean project and select Build.

      This command creates a JAR file that contains the bean class file.

  2. To build the multiclient module, follow these steps:

    1. Select File->Open Project.

    2. Select the multiclient folder.

    3. Select the Open as Main Project check box.

    4. Click Open Project.

    5. In the Projects tab, right-click the multiclient project and select Build.

      This command creates a JAR file that contains the client class file and a manifest file.

  3. To deploy the multiclient module on the local server, follow these steps:

    1. Right-click the multiclient project and select Properties.

    2. Select Run from the Categories tree.

    3. From the Server list, select GlassFish v3 Domain (the local server).

    4. Click OK.

    5. Right-click the multiclient project and select Deploy.

      You can use the Services tab to verify that multiclient is deployed as an App Client Module on the local server.

  4. To deploy the replybean module on the local and remote servers, follow these steps:

    1. Right-click the replybean project and select Properties.

    2. Select Run from the Categories tree.

    3. From the Server list, select GlassFish v3 Domain (the local server).

    4. Click OK.

    5. Right-click the replybean project and select Deploy.

    6. Right-click the replybean project again and select Properties.

    7. Select Run from the Categories tree.

    8. From the Server list, select GlassFish v3 (2) (the remote server).

    9. Click OK.

    10. Right-click the replybean project and select Deploy.

      You can use the Services tab to verify that replybean is deployed as an EJB Module on both servers.

  5. To run the application client, right-click the multiclient project and select Run Project.

    This command returns a JAR file named multiclientClient.jar and then executes it.

    On the local system, the output of the appclient command looks something like this:


    running application client container.
    Sent message: text: id=1 to local app server
    Sent message: text: id=2 to remote app server
    ReplyListener: Received message: id=1, text=ReplyMsgBean processed message: text: id=1 to local
     app server
    Sent message: text: id=3 to local app server
    ReplyListener: Received message: id=3, text=ReplyMsgBean processed message: text: id=3 to local
     app server
    ReplyListener: Received message: id=2, text=ReplyMsgBean processed message: text: id=2 to remote
     app server
    Sent message: text: id=4 to remote app server
    ReplyListener: Received message: id=4, text=ReplyMsgBean processed message: text: id=4 to remote
     app server
    Sent message: text: id=5 to local app server
    ReplyListener: Received message: id=5, text=ReplyMsgBean processed message: text: id=5 to local
     app server
    Sent message: text: id=6 to remote app server
    ReplyListener: Received message: id=6, text=ReplyMsgBean processed message: text: id=6 to remote
     app server
    Sent message: text: id=7 to local app server
    ReplyListener: Received message: id=7, text=ReplyMsgBean processed message: text: id=7 to local
     app server
    Sent message: text: id=8 to remote app server
    ReplyListener: Received message: id=8, text=ReplyMsgBean processed message: text: id=8 to remote
     app server
    Sent message: text: id=9 to local app server
    ReplyListener: Received message: id=9, text=ReplyMsgBean processed message: text: id=9 to local
     app server
    Sent message: text: id=10 to remote app server
    ReplyListener: Received message: id=10, text=ReplyMsgBean processed message: text: id=10 to remote
     app server
    Waiting for 0 message(s) from local app server
    Waiting for 0 message(s) from remote app server
    Finished
    Closing connection 1
    Closing connection 2

    On the local system, where the message-driven bean receives the odd-numbered messages, the output in the server log looks like this (wrapped in logging information):


    ReplyMsgBean: Received message: text: id=1 to local app server
    ReplyMsgBean: Received message: text: id=3 to local app server
    ReplyMsgBean: Received message: text: id=5 to local app server
    ReplyMsgBean: Received message: text: id=7 to local app server
    ReplyMsgBean: Received message: text: id=9 to local app server

    On the remote system, where the bean receives the even-numbered messages, the output in the server log looks like this (wrapped in logging information):


    ReplyMsgBean: Received message: text: id=2 to remote app server
    ReplyMsgBean: Received message: text: id=4 to remote app server
    ReplyMsgBean: Received message: text: id=6 to remote app server
    ReplyMsgBean: Received message: text: id=8 to remote app server
    ReplyMsgBean: Received message: text: id=10 to remote app server

ProcedureTo Build, Package, Deploy, and Run the sendremote Modules Using Ant

  1. To package the modules, follow these steps:

    1. Go to the following directory:

      tut-install/examples/jms/sendremote/multiclient/
      
    2. Type the following command:


      ant
      

      This command creates a JAR file that contains the client class file and a manifest file.

    3. Change to the directory replybean:


      cd ../replybean
      
    4. Type the following command:


      ant
      

      This command creates a JAR file that contains the bean class file.

  2. To deploy the replybean module on the local and remote servers, follow these steps:

    1. Verify that you are still in the directory replybean.

    2. Type the following command:


      ant deploy
      

      Ignore the message that states that the application is deployed at a URL.

    3. Type the following command:


      ant deploy-remote -Dsys=remote-system-name
      

      Replace remote-system-name with the actual name of the remote system.

  3. To deploy and run the client, follow these steps:

    1. Change to the directory multiclient:


      cd ../multiclient
      
    2. Type the following command:


      ant run
      

      On the local system, the output looks something like this:


      running application client container.
      Sent message: text: id=1 to local app server
      Sent message: text: id=2 to remote app server
      ReplyListener: Received message: id=1, text=ReplyMsgBean processed message: text: id=1 to local
       app server
      Sent message: text: id=3 to local app server
      ReplyListener: Received message: id=3, text=ReplyMsgBean processed message: text: id=3 to local
       app server
      ReplyListener: Received message: id=2, text=ReplyMsgBean processed message: text: id=2 to remote
       app server
      Sent message: text: id=4 to remote app server
      ReplyListener: Received message: id=4, text=ReplyMsgBean processed message: text: id=4 to remote
       app server
      Sent message: text: id=5 to local app server
      ReplyListener: Received message: id=5, text=ReplyMsgBean processed message: text: id=5 to local
       app server
      Sent message: text: id=6 to remote app server
      ReplyListener: Received message: id=6, text=ReplyMsgBean processed message: text: id=6 to remote
       app server
      Sent message: text: id=7 to local app server
      ReplyListener: Received message: id=7, text=ReplyMsgBean processed message: text: id=7 to local
       app server
      Sent message: text: id=8 to remote app server
      ReplyListener: Received message: id=8, text=ReplyMsgBean processed message: text: id=8 to remote
       app server
      Sent message: text: id=9 to local app server
      ReplyListener: Received message: id=9, text=ReplyMsgBean processed message: text: id=9 to local
       app server
      Sent message: text: id=10 to remote app server
      ReplyListener: Received message: id=10, text=ReplyMsgBean processed message: text: id=10 to remote
       app server
      Waiting for 0 message(s) from local app server
      Waiting for 0 message(s) from remote app server
      Finished
      Closing connection 1
      Closing connection 2

      On the local system, where the message-driven bean receives the odd-numbered messages, the output in the server log looks like this (wrapped in logging information):


      ReplyMsgBean: Received message: text: id=1 to local app server
      ReplyMsgBean: Received message: text: id=3 to local app server
      ReplyMsgBean: Received message: text: id=5 to local app server
      ReplyMsgBean: Received message: text: id=7 to local app server
      ReplyMsgBean: Received message: text: id=9 to local app server

      On the remote system, where the bean receives the even-numbered messages, the output in the server log looks like this (wrapped in logging information):


      ReplyMsgBean: Received message: text: id=2 to remote app server
      ReplyMsgBean: Received message: text: id=4 to remote app server
      ReplyMsgBean: Received message: text: id=6 to remote app server
      ReplyMsgBean: Received message: text: id=8 to remote app server
      ReplyMsgBean: Received message: text: id=10 to remote app server

Chapter 32 Advanced Bean Validation Concepts and Examples

This chapter describes how to create custom constraints, custom validator messages, and constraint groups using the Java API for JavaBeans Validation (Bean Validation).

The following topics are addressed here:

Creating Custom Constraints

Bean Validation defines annotations, interfaces, and classes to allow developers to create custom constraints.

Using the Built-In Constraints To Make a New Constraint

Bean Validation includes several built-in constraints that can be combined to create new, reusable constraints. This can simplify constraint definitions by allowing developers to define a custom constraint made up of several built-in constraints that may then be applied to component attributes with a single annotation.


Example 32–1 The @Email Constraint

@Pattern.List({
    @Pattern(regexp = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\."
        +"[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*"
        +"@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
})
@Constraint(validatedBy = {})
@Documented
@Target({ElementType.METHOD,
    ElementType.FIELD,
    ElementType.ANNOTATION_TYPE,
    ElementType.CONSTRUCTOR,
    ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Email {

    String message() default "{invalid.email}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ElementType.METHOD,
        ElementType.FIELD,
        ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR,
        ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        Email[] value();
    }
}

This custom constraint can then be applied to an attribute.

...
@Email
protected String email;
...

Customizing Validator Messages

Bean Validation includes a resource bundle of default messages for the build-in constraints. These messages can be customized, and localized for non-English speaking locales.

The ValidationMessages Resource Bundle

The Validationmessages resource bundle and the locale variants of this resource bundle contain strings that override the default validation messages. The ValidationMessages resource bundle is typically a properties file, ValidationMessages.properties, in the default package of an application.

Localizing Validation Messages

Locale variants of ValidationMessages.properties are added by appending an underscore and the locale prefix. For example, the Spanish locale variant resource bundle would be ValidationMessages_es.properties.

Grouping Constraints

Constraints may be added to one or more groups. Constraint groups are used to create subsets of constraints, so only certain constraints will be validated for a particular object. By default, all constraints are included in the Default constraint group.

Constraint groups are represented by interfaces.

public interface Employee {}

public interface Contractor {}

Constraint groups can inherit from other groups.

public interface Manager extends Employee {}

When a constraint is added to an element, the constraint declares which groups that constraint belongs by specifying the class name of the group interface name in the groups element of the constraint.

@NotNull(groups=Employee.class)
Phone workPhone;

Multiple groups can be declared by surrounding the groups with angle brackets ({ and }) and separating the groups class names with commas.

@NotNull(groups={ Employee.class, Contractor.class })
Phone workPhone;

If a group inherits from another group, validating that group results in validating all constraints declared as part of the supergroup. For example, validating the Manager group results in the workPhone field being validated, because Employee is a super-interface of Manager.

Customizing Group Validation Order

By default, constraint groups are validated in no particular order. There are some cases where some groups should be validated before others. For example, in a particular class basic data should be validated before more advanced data.

To set the validation order for a group, add a javax.validation.GroupSequence annotation on the interface definition, listing the order in which the validation should occur.

@GroupSequence({Default.class, ExpensiveValidationGroup.class})
public interface FullValidationGroup {}

When validating FullValidationGroup, first the Default group is validated. If all the data passes validation, then the ExpensiveValidationGroup group is validated. If a constraint is part of part of both the Default and ExpensiveValidationGroup groups, the constraint is validated as part of the Default group, and will not be validated on the subsequent ExpensiveValidationGroup pass.