Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Developer's Guide

Part III Using Services and APIs

Chapter 11 Using the JDBC API for Database Access

This chapter describes how to use the JavaTM Database Connectivity (JDBCTM) API for database access with the Sun Java System Application Server. This chapter also provides high level JDBC implementation instructions for servlets and EJBTM components using the Application Server. The Application Server supports the JDBC 3.0 API, which encompasses the JDBC 2.0 Optional Package API.

The JDBC specifications are available at http://java.sun.com/products/jdbc/download.html.

A useful JDBC tutorial is located at http://java.sun.com/docs/books/tutorial/jdbc/index.html.

For explanations of two-tier and three-tier database access models, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide.


Note –

The Application Server does not support connection pooling or transactions for an application’s database access if it does not use standard J2EETM DataSource objects.


This chapter discusses the following topics:

General Steps for Creating a JDBC Resource

To prepare a JDBC resource for use in J2EE applications deployed to the Application Server, perform the following tasks:

For information about how to configure some specific JDBC drivers, see the Configurations for Specific JDBC Drivers.

Integrating the JDBC Driver

To use JDBC features, you must choose a JDBC driver to work with the Application Server, then you must set up the driver. This section covers these topics:

Supported Database Drivers

Supported JDBC drivers are those that have been fully tested by Sun. For a list of the JDBC drivers currently supported by the Application Server, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Release Notes. For configurations of supported and other drivers, see Configurations for Specific JDBC Drivers.


Note –

Because the drivers and databases supported by the Application Server are constantly being updated, and because database vendors continue to upgrade their products, always check with Sun technical support for the latest database support information.


Making the JDBC Driver JAR Files Accessible

To integrate the JDBC driver into a Application Server domain, copy the JAR files into the domain-dir/lib/ext directory, then restart the server. This makes classes accessible to any application or module across the domain. For more information about Application Server classloaders, see Classloaders.

Creating a Connection Pool

When you create a connection pool that uses JDBC technology (a JDBC connection pool) in the Application Server, you can define many of the characteristics of your database connections.

You can create a JDBC connection pool in one of these ways:

Testing a Connection Pool

You can test a JDBC connection pool for usability in one of these ways:

Both these commands fail and display an error message unless they successfully connect to the connection pool.

For information about how to tune a connection pool, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Performance Tuning Guide.

Creating a JDBC Resource

A JDBC resource, also called a data source, lets you make connections to a database using getConnection(). Create a JDBC resource in one of these ways:

Creating Applications That Use the JDBC API

An application that uses the JDBC API is an application that looks up and connects to one or more databases. This section covers these topics:

Sharing Connections

When multiple connections acquired by an application use the same JDBC resource, the connection pool provides connection sharing within the same transaction scope. For example, suppose Bean A starts a transaction and obtains a connection, then calls a method in Bean B. If Bean B acquires a connection to the same JDBC resource with the same sign-on information, and if Bean A completes the transaction, the connection can be shared.

Connections obtained through a resource are shared only if the resource reference declared by the J2EE component allows it to be shareable. This is specified in a component’s deployment descriptor by setting the res-sharing-scope element to Shareable for the particular resource reference. To turn off connection sharing, set res-sharing-scope to Unshareable.

For general information about connections and JDBC URLs, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide.

Obtaining a Physical Connection from a Wrapped Connection

The DataSource implementation in the Application Server provides a getConnection method that retrieves the JDBC driver’s SQLConnection from the Application Server’s Connection wrapper. The method signature is as follows:

public java.sql.Connection getConnection(java.sql.Connection con) 
throws java.sql.SQLException

For example:

InitialContext ctx = new InitialContext();
com.sun.appserv.DataSource ds = (com.sun.appserv.DataSource) 
   ctx.lookup("jdbc/MyBase");
Connection con = ds.getConnection();
Connection drivercon = ds.getConnection(con);
// Do db operations.
con.close();

Using Non-Transactional Connections

The DataSource implementation in the Application Server provides a getNonTxConnection method, which retrieves a JDBC connection that is not in the scope of any transaction. There are two variants, as follows:

public java.sql.Connection getNonTxConnection() throws java.sql.SQLException
public java.sql.Connection getNonTxConnection(String user, String password) 
throws java.sql.SQLException

Another way to get a non-transactional connection is to create a resource with the JNDI name ending in __nontx. This forces all connections looked up using this resource to be non transactional.

Typically, a connection is enlisted in the context of the transaction in which a getConnection call is invoked. However, a non-transactional connection is not enlisted in a transaction context even if a transaction is in progress.

The main advantage of using non-transactional connections is that the overhead incurred in enlisting and delisting connections in transaction contexts is avoided. However, use such connections carefully. For example, if a non-transactional connection is used to query the database while a transaction is in progress that modifies the database, the query retrieves the unmodified data in the database. This is because the in-progress transaction hasn’t committed. For another example, if a non-transactional connection modifies the database and a transaction that is running simultaneously rolls back, the changes made by the non-transactional connection are not rolled back.

Here is a typical use case for a non-transactional connection: a component that is updating a database in a transaction context spanning over several iterations of a loop can refresh cached data by using a non-transactional connection to read data before the transaction commits.

Using JDBC Transaction Isolation Levels

For general information about transactions, see Chapter 12, Using the Transaction Serviceand the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide. For information about last agent optimization, which can improve performance, see Transaction Scope.

Not all database vendors support all transaction isolation levels available in the JDBC API. The Application Server permits specifying any isolation level your database supports. The following table defines transaction isolation levels.

Table 11–1 Transaction Isolation Levels

Transaction Isolation Level  

Description  

TRANSACTION_READ_UNCOMMITTED

Dirty reads, non-repeatable reads and phantom reads can occur. 

TRANSACTION_READ_COMMITTED

Dirty reads are prevented; non-repeatable reads and phantom reads can occur. 

TRANSACTION_REPEATABLE_READ

Dirty reads and non-repeatable reads are prevented; phantom reads can occur. 

TRANSACTION_SERIALIZABLE

Dirty reads, non-repeatable reads and phantom reads are prevented. 

Note that you cannot call setTransactionIsolation() during a transaction.

You can set the default transaction isolation level for a JDBC connection pool. For details, see Creating a Connection Pool.

To verify that a level is supported by your database management system, test your database programmatically using the supportsTransactionIsolationLevel() method in java.sql.DatabaseMetaData, as shown in the following example:

java.sql.DatabaseMetaData db;
if (db.supportsTransactionIsolationLevel(TRANSACTION_SERIALIZABLE)
   { Connection.setTransactionIsolation(TRANSACTION_SERIALIZABLE); }

For more information about these isolation levels and what they mean, see the JDBC 3.0 API specification.


Note –

Applications that change the isolation level on a pooled connection programmatically risk polluting the pool, which can lead to errors.


Configurations for Specific JDBC Drivers

Application Server 8.1 is designed to support connectivity to any database management system with a corresponding JDBC driver. The following JDBC driver and database combinations are supported. These combinations have been tested with Application Server 8.1 and are found to be J2EE compatible. They are also supported for CMP.

For an up to date list of currently supported JDBC drivers, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Release Notes.

Other JDBC drivers can be used with Application Server 8.1, but J2EE compliance tests have not been completed with these drivers. Although Sun offers no product support for these drivers, Sun offers limited support of the use of these drivers with Application Server 8.1.

For details about how to integrate a JDBC driver and how to use the Administration Console or the command line interface to implement the configuration, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide.


Note –

An Oracle database user running the capture-schema command needs ANALYZE ANY TABLE privileges if that user does not own the schema. These privileges are granted to the user by the database administrator. For information about capture-schema, see Using the capture-schema Utility.


PointBase Type 4 Driver

The PointBase JDBC driver is included with the Application Server by default, except for the Solaris bundled installation, which does not include PointBase. Therefore, unless you have the Solaris bundled installation, you do not need to integrate this JDBC driver with the Application Server.

PointBase is intended for evaluation use only, not for production or deployment use.

The JAR file for the PointBase driver is pbclient.jar.

Configure the connection pool using the following settings:

Sun Java System JDBC Driver for DB2 Databases

The JAR files for this driver are smbase.jar, smdb2.jar, and smutil.jar. Configure the connection pool using the following settings:

Sun Java System JDBC Driver for Oracle 8.1.7 and 9.x Databases

The JAR files for this driver are smbase.jar, smoracle.jar, and smutil.jar. Configure the connection pool using the following settings:

Sun Java System JDBC Driver for Microsoft SQL Server Databases

The JAR files for this driver are smbase.jar, smsqlserver.jar, and smutil.jar. Configure the connection pool using the following settings:

Sun Java System JDBC Driver for Sybase Databases

The JAR files for this driver are smbase.jar, smsybase.jar, and smutil.jar. Configure the connection pool using the following settings:

IBM DB2 8.1 Type 2 Driver

The JAR files for the DB2 driver are db2jcc.jar, db2jcc_license_cu.jar, and db2java.zip. Set environment variables as follows:

LD_LIBRARY_PATH=/usr/db2user/sqllib/lib:${j2ee.home}/lib
DB2DIR=/opt/IBM/db2/V8.1
DB2INSTANCE=db2user
INSTHOME=/usr/db2user
VWSPATH=/usr/db2user/sqllib
THREADS_FLAG=native

Configure the connection pool using the following settings:

JConnect Type 4 Driver for Sybase ASE 12.5 Databases

The JAR file for the Sybase driver is jconn2.jar. Configure the connection pool using the following settings:

Inet Oraxo JDBC Driver for Oracle 8.1.7 and 9.x Databases

The JAR file for the Inet Oracle driver is Oranxo.jar. Configure the connection pool using the following settings:

Inet Merlia JDBC Driver for Microsoft SQL Server Databases

The JAR file for the Inet Microsoft SQL Server driver is Merlia.jar. Configure the connection pool using the following settings:

Inet Sybelux JDBC Driver for Sybase Databases

The JAR file for the Inet Sybase driver is Sybelux.jar. Configure the connection pool using the following settings:

Oracle Thin Type 4 Driver for Oracle 8.1.7 and 9.x Databases

The JAR file for the Oracle driver is ojdbc14.jar. Configure the connection pool using the following settings:

OCI Oracle Type 2 Driver for Oracle 8.1.7 and 9.x Databases

The JAR file for the OCI Oracle driver is ojdbc14.jar. Make sure that the shared library is available through LD_LIBRARY_PATH and that the ORACLE_HOME property is set. Configure the connection pool using the following settings:

IBM Informix Type 4 Driver

Configure the connection pool using the following settings:

MM MySQL Type 4 Driver

Configure the connection pool using the following settings:

CloudScape 5.1 Type 4 Driver

The JAR files for the CloudScape driver are db2j.jar, db2jtools.jar, db2jcview.jar, jh.jar, db2jcc.jar, and db2jnet.jar. Configure the connection pool using the following settings:

Chapter 12 Using the Transaction Service

The J2EE platform provides several abstractions that simplify development of dependable transaction processing for applications. This chapter discusses J2EE transactions and transaction support in the Sun Java System Application Server.

This chapter contains the following sections:

For more information about the JavaTM Transaction API (JTA) and Java Transaction Service (JTS), see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide and the following sites: http://java.sun.com/products/jta/ and http://java.sun.com/products/jts/.

You might also want to read the chapter on transactions in the J2EE 1.4 Tutorial at http://java.sun.com/j2ee/1.4/docs/tutorial/doc/index.html.

Transaction Resource Managers

There are three types of transaction resource managers:

For details about how transaction resource managers, the transaction service, and applications interact, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide.


Note –

In the Application Server, the transaction manager is a privileged interface. However, applications can access UserTransaction. For more information, see Naming Environment for J2EE Application Components.


Transaction Scope

A local transaction involves only one non-XA resource and requires that all participating application components execute within one process. Local transaction optimization is specific to the resource manager and is transparent to the J2EE application.

In the Application Server, a JDBC resource is non-XA if it meets any of the following criteria:

A transaction remains local if the following conditions remain true:

Transactions that involve multiple resources or multiple participant processes are distributed or global transactions. A global transaction can involve one non-XA resource if last agent optimization is enabled. Otherwise, all resourced must be XA. The use-last-agent-optimization property is set to true by default. For details about how to set this property, see Configuring the Transaction Service.

If only one XA resource is used in a transaction, one-phase commit occurs, otherwise the transaction is coordinated with a two-phase commit protocol.

A two-phase commit protocol between the transaction manager and all the resources enlisted for a transaction ensures that either all the resource managers commit the transaction or they all abort. When the application requests the commitment of a transaction, the transaction manager issues a PREPARE_TO_COMMIT request to all the resource managers involved. Each of these resources can in turn send a reply indicating whether it is ready for commit (PREPARED) or not (NO). Only when all the resource managers are ready for a commit does the transaction manager issue a commit request (COMMIT) to all the resource managers. Otherwise, the transaction manager issues a rollback request (ABORT) and the transaction is rolled back.

The Application Server provides workarounds for some known issues with the recovery implementations of the following JDBC drivers. These workarounds are used unless explicitly disabled.


Note –

These workarounds do not imply support for any particular JDBC driver.


Configuring the Transaction Service

You can configure the transaction service in the Application Server in the following ways:

Transaction Logging

The transaction service writes transactional activity into transaction logs so that transactions can be recovered. You can control transaction logging in these ways:

Chapter 13 Using the Java Naming and Directory Interface

A naming service maintains a set of bindings, which relate names to objects. The J2EETM naming service is based on the Java Naming and Directory InterfaceTM (JNDI) API. The JNDI API allows application components and clients to look up distributed resources, services, and EJBTM components. For general information about the JNDI API, see http://java.sun.com/products/jndi/.

You can also see the JNDI tutorial at http://java.sun.com/products/jndi/tutorial/.

This chapter contains the following sections:

Accessing the Naming Context

The Application Server provides a naming environment, or context, which is compliant with standard J2EE 1.4 requirements. A Context object provides the methods for binding names to objects, unbinding names from objects, renaming objects, and listing the bindings. The InitialContext is the handle to the J2EE naming service that application components and clients use for lookups.

The JNDI API also provides subcontext functionality. Much like a directory in a file system, a subcontext is a context within a context. This hierarchical structure permits better organization of information. For naming services that support subcontexts, the Context class also provides methods for creating and destroying subcontexts.

The rest of this section covers these topics:


Note –

Each resource within a server instance must have a unique name. However, two resources in different server instances or different domains can have the same name.


Naming Environment for J2EE Application Components

The namespace for objects looked up in a J2EE environment is organized into different subcontexts, with the standard prefix java:comp/env.

The following table describes standard JNDI subcontexts for connection factories in the Application Server.

Table 13–1 Standard JNDI Subcontexts for Connection Factories

Resource Manager  

Connection Factory Type  

JNDI Subcontext  

JDBCTM

javax.sql.DataSource

java:comp/env/jdbc

Transaction Service

javax.transaction.UserTransaction

java:comp/UserTransaction

JMS

javax.jms.TopicConnectionFactory

javax.jms.QueueConnectionFactory

java:comp/env/jms

JavaMailTM

javax.mail.Session

java:comp/env/mail

URL

java.net.URL

java:comp/env/url

Connector

javax.resource.cci.ConnectionFactory

java:comp/env/eis

Accessing EJB Components Using the CosNaming Naming Context

The preferred way of accessing the naming service, even in code that runs outside of a J2EE container, is to use the no-argument InitialContext constructor. However, if EJB client code explicitly instantiates an InitialContext that points to the CosNaming naming service, it is necessary to set these properties in the client JVM when accessing EJB components:

-Djavax.rmi.CORBA.UtilClass=com.sun.corba.ee.impl.javax.rmi.CORBA.Util
-Dorg.omg.CORBA.ORBClass=com.sun.corba.ee.impl.orb.ORBImpl
-Dorg.omg.CORBA.ORBSingletonClass=com.sun.corba.ee.impl.orb.ORBSingleton
-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory

Accessing EJB Components in a Remote Application Server

The recommended approach for looking up an EJB component in a remote Application Server from a client that is a servlet or EJB component is to use the Interoperable Naming Service syntax. Host and port information is prepended to any global JNDI names and is automatically resolved during the lookup. The syntax for an interoperable global name is as follows:

corbaname:iiop:host:port#a/b/name

This makes the programming model for accessing EJB components in another Application Server exactly the same as accessing them in the same server. The deployer can change the way the EJB components are physically distributed without having to change the code.

For J2EE components, the code still performs a java:comp/env lookup on an EJB reference. The only difference is that the deployer maps the ejb-reference element to an interoperable name in an Application Server deployment descriptor file instead of a simple global JNDI name.

For example, suppose a servlet looks up an EJB reference using java:comp/env/ejb/Foo, and the target EJB component has a global JNDI name of a/b/Foo.

The ejb-ref element in sun-web.xml looks like this:

<ejb-ref>
   <ejb-ref-name>ejb/Foo</ejb-ref-name>
   <jndi-name>corbaname:iiop:host:port#a/b/Foo</jndi-name>
<ejb-ref>

The code looks like this:

Context ic = new InitialContext();
Object o = ic.lookup("java:comp/env/ejb/Foo");

For a client that doesn’t run within a J2EE container, the code just uses the interoperable global name instead of the simple global JNDI name. For example:

Context ic = new InitialContext();
Object o = ic.lookup("corbaname:iiop:host:port#a/b/Foo");

Objects stored in the interoperable naming context and component-specific (java:comp/env) naming contexts are transient. On each server startup or application reloading, all relevant objects are re-bound to the namespace.

Naming Environment for Lifecycle Modules

Lifecycle listener modules provide a means of running short or long duration Java-based tasks within the application server environment, such as instantiation of singletons or RMI servers. These modules are automatically initiated at server startup and are notified at various phases of the server life cycle. For details about lifecycle modules, see Chapter 10, Developing Lifecycle Listeners.

The configured properties for a lifecycle module are passed as properties during server initialization (the INIT_EVENT). The initial JNDI naming context is not available until server initialization is complete. A lifecycle module can get the InitialContext for lookups using the method LifecycleEventContext.getInitialContext() during, and only during, the STARTUP_EVENT, READY_EVENT, or SHUTDOWN_EVENT server life cycle events.

Configuring Resources

The Application Server exposes the following special resources in the naming environment. Full administration details are provided in the following sections:

External JNDI Resources

An external JNDI resource defines custom JNDI contexts and implements the javax.naming.spi.InitialContextFactory interface. There is no specific JNDI parent context for external JNDI resources, except for the standard java:comp/env/.

Create an external JNDI resource in one of these ways:

Custom Resources

A custom resource specifies a custom server-wide resource object factory that implements the javax.naming.spi.ObjectFactory interface. There is no specific JNDI parent context for external JNDI resources, except for the standard java:comp/env/.

Create a custom resource in one of these ways:

Mapping References

The following XML elements map JNDI names configured in the Application Server to resource references in application client, EJB, and web application components:

JNDI names for EJB components must be unique. For example, appending the application name and the module name to the EJB name is one way to guarantee unique names. In this case, mycompany.pkging.pkgingEJB.MyEJB would be the JNDI name for an EJB in the module pkgingEJB.jar, which is packaged in the pkging.ear application.

These elements are part of the sun-web-app.xml, sun-ejb-ref.xml, and sun-application-client.xml deployment descriptor files. For more information about how these elements behave in each of the deployment descriptor files, see Appendix A, Deployment Descriptor Files.

The rest of this section uses an example of a JDBC resource lookup to describe how to reference resource factories. The same principle is applicable to all resources (such as JMS destinations, JavaMail sessions, and so on).

The resource-ref element in the sun-web-app.xml deployment descriptor file maps the JNDI name of a resource reference to the resource-ref element in the web-app.xml J2EE deployment descriptor file.

The resource lookup in the application code looks like this:

InitialContext ic = new InitialContext();
String dsName = "java:comp/env/jdbc/HelloDbDs";
DataSource ds = (javax.sql.DataSource)ic.lookup(dsName);
Connection connection = ds.getConnection();

The resource being queried is listed in the res-ref-name element of the web.xml file as follows:

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

The resource-ref section in a Application Server specific deployment descriptor, for example sun-web.xml, maps the res-ref-name (the name being queried in the application code) to the JNDI name of the JDBC resource. The JNDI name is the same as the name of the JDBC resource as defined in the resource file when the resource is created.

<resource-ref>
   <res-ref-name>jdbc/HelloDbDs</res-ref-name>
   <jndi-name>jdbc/HelloDbDataSource</jndi-name>
</resource-ref>

The JNDI name in the Application Server specific deployment descriptor must match the JNDI name you assigned to the resource when you created and configured it.

Chapter 14 Using the Java Message Service

This chapter describes how to use the JavaTM Message Service (JMS) API. The Sun Java System Application Server has a fully integrated JMS provider: the Sun Java System Message Queue software.

For general information about the JMS API, see the J2EE 1.4 Tutorial at http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JMS.html#wp84181.

For detailed information about JMS concepts and JMS support in the Application Server, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide.

This chapter contains the following sections:

The JMS Provider

The Application Server support for JMS messaging, in general, and for message-driven beans, in particular, requires messaging middleware that implements the JMS specification: a JMS provider. The Application Server uses the Sun Java System Message Queue software as its native JMS provider. The Message Queue software is tightly integrated into theApplication Server, providing transparent JMS messaging support. This support is known within Application Server as the JMS Service. The JMS Service requires only minimal administration.

The relationship of the Message Queue software to the Application Server can be one of these types: LOCAL or REMOTE. The results of these choices and their interactions with clustering are as follows:

For more information about setting the type and the default JMS host, see Configuring the JMS Service.

For more information about the Message Queue software, refer to the documentation at http://docs.sun.com/app/docs/coll/1307.1.

For general information about the JMS API, see the JMS web page at http://java.sun.com/products/jms/index.html.

Message Queue Resource Adapter

The Sun Java System Message Queue software is integrated into the Application Server using a resource adapter that is compliant with the Connector 1.5 specification. The module name of this system resource adapter is jmsra. Every JMS resource is converted to a corresponding connector resource of this resource adapter as follows:

You can use connector configuration tools to manage JMS resources. For more information, see Chapter 9, Developing Connectors.

Administration of the JMS Service

To configure the JMS Service and prepare JMS resources for use in applications deployed to the Application Server, you must perform these tasks:

For more information about JMS administration tasks, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide and the Sun Java System Message Queue 3 2005Q4 Administration Guide.

Configuring the JMS Service

The JMS Service configuration is available to all inbound and outbound connections pertaining to the Application Server cluster or instance. You can edit the JMS Service configuration in the following ways:

You can override the JMS Service configuration using JMS connection factory settings. For details, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide.


Note –

The Application Server instance must be restarted after configuration of the JMS Service.


The Default JMS Host

A JMS host refers to a Sun Java System Message Queue broker. A default JMS host for the JMS service is provided, named default_JMS_host. This is the JMS host that the Application Server instance starts when the JMS Service type is configured as LOCAL.

If you have created a multi-broker cluster in the Message Queue software, delete the default JMS host, then add the Message Queue cluster’s brokers as JMS hosts. In this case, the default JMS host becomes the first JMS host in the AddressList. (For more information about the AddressList, see JMS Connection Features. You can also explicitly set the default JMS host; see Configuring the JMS Service.

When the Application Server uses a Message Queue cluster, it executes Message Queue specific commands on the default JMS host. For example, when a physical destination is created for a Message Queue cluster of three brokers, the command to create the physical destination is executed on the default JMS host, but the physical destination is used by all three brokers in the cluster.

Creating JMS Hosts

You can create additional JMS hosts in the following ways:

Checking Whether the JMS Provider Is Running

You can use the asadmin jms-ping command to check whether a Sun Java System Message Queue instance is running. For details, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Reference Manual.

Creating Physical Destinations

Produced messages are delivered for routing and subsequent delivery to consumers using physical destinations in the JMS provider. A physical destination is identified and encapsulated by an administered object (a Topic or Queue destination resource) that an application component uses to specify the destination of messages it is producing and the source of messages it is consuming.

If a message-driven bean is deployed and the physical destination it listens to doesn’t exist, the Application Server automatically creates the physical destination and sets the value of the property maxNumActiveConsumers to -1 (see Load-Balanced Message Inflow). However, it is good practice to create the physical destination beforehand.

You can create a JMS physical destination in the following ways:

To create a destination resource, see Creating JMS Resources: Destinations and Connection Factories.

Creating JMS Resources: Destinations and Connection Factories

You can create two kinds of JMS resources in the Application Server:

In either case, the steps for creating a JMS resource are the same. You can create a JMS resource in the following ways:


Note –

When a Queue is automatically created for a message-driven bean deployed to an Application Server cluster, the value of the property maxNumActiveConsumers is set to -1 so that multiple consumers can access the Queue at the same time. For more information, see Load-Balanced Message Inflow.

All JMS resource properties that used to work with version 7 of the Application Server are supported for backward compatibility.


Restarting the JMS Client After JMS Configuration

When a JMS client accesses a JMS administered object for the first time, the client JVM retrieves the JMS service configuration from the Application Server. Further changes to the configuration are not available to the client JVM until the client is restarted.

JMS Connection Features

The Sun Java System Message Queue software supports the following JMS connection features:

Both these features use the AddressList configuration, which is populated with the hosts and ports of the JMS hosts defined in the Application Server. The AddressList is updated whenever a JMS host configuration changes. The AddressList is inherited by any JMS resource when it is created and by any MDB when it is deployed.


Note –

In the Sun Java System Message Queue software, the AddressList property is called imqAddressList.


Connection Pooling

The Application Server pools JMS connections automatically.

To dynamically modify connection pool properties using the Administration Console, go to either the Connection Factories page (see Creating JMS Resources: Destinations and Connection Factories) or the Connector Connection Pools page (see Deploying and Configuring a Stand-Alone Connector Module).

To use the command line, use the asadmin create-connector-connection-pool command to manage the pool (see Deploying and Configuring a Stand-Alone Connector Module.

The addresslist-behavior JMS service attribute is set to random by default. This means that each ManagedConnection (physical connection) created from the ManagedConnectionFactory selects its primary broker in a random way from the AddressList.

When a JMS connection pool is created, there is one ManagedConnectionFactory instance associated with it. If you configure the AddressList as a ManagedConnectionFactory property, the AddressList configuration in the ManagedConnectionFactory takes precedence over the one defined in the Application Server.

Connection Failover

To specify whether the Application Server tries to reconnect to the primary broker if the connection is lost, set the reconnect-enabled attribute in the JMS service. To specify the number of retries and the time between retries, set the reconnect-attempts and reconnect-interval-in-seconds attributes, respectively.

If reconnection is enabled and the primary broker goes down, the Application Server tries to reconnect to another broker in the AddressList. The AddressList is updated whenever a JMS host configuration changes. The logic for scanning is decided by two JMS service attributes, addresslist-behavior and addresslist-iterations.

You can override these settings using JMS connection factory settings. For details, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide.

The Sun Java System Message Queue software transparently transfers the load to another broker when the failover occurs. JMS semantics are maintained during failover.

Load-Balanced Message Inflow

You can configure ActivationSpec properties of the jmsra resource adapter in the sun-ejb-jar.xml file for a message-driven bean using activation-config-property elements. Whenever a message-driven bean (EndPointFactory) is deployed, the connector runtime engine finds these properties and configures them accordingly in the resource adapter.

The Application Server transparently enables messages to be delivered in random fashion to message-driven beans having same ClientID. The ClientID is required for durable subscribers.

For nondurable subscribers in which the ClientID is not configured, all instances of a specific message-driven bean that subscribe to same topic are considered equal. When a message-driven bean is deployed to multiple instances of the Application Server, only one of the message-driven beans receives the message. If multiple distinct message-driven beans subscribe to same topic, one instance of each message-driven bean receives a copy of the message.

To support multiple consumers using the same queue, set the maxNumActiveConsumers property of the physical destination to a large value. If this property is set, the Sun Java System Message Queue software allows multiple message-driven beans to consume messages from same queue. The message is delivered randomly to the message-driven beans. If maxNumActiveConsumers is set to -1, there is no limit to the number of consumers.

The following sample application demonstrates load-balanced message inflow:

install-dir/samples/ee-samples/failover/apps/mqfailover

Transactions and Non-Persistent Messages

During transaction recovery, non-persistent messages might be lost. If the broker fails between the transaction manager’s prepare and commit operations, any non-persistent message in the transaction is lost and cannot be delivered. A message that is not saved to a persistent store is not available for transaction recovery.

ConnectionFactory Authentication

If your web, EJB, or client module has res-auth set to Container, but you use the ConnectionFactory.createConnection("user","password") method to get a connection, the Application Server searches the container for authentication information before using the supplied user and password. Version 7 of the Application Server threw an exception in this situation.

Message Queue varhome Directory

The Sun Java System Message Queue software uses a default directory for storing data such as persistent messages and its log file. This directory is called varhome. The Application Server uses domain-dir/imq as the varhome directory if the type of relationship between the Application Server and the Message Queue software is LOCAL or EMBEDDED. If the relationship type is REMOTE, the Message Queue software determines the varhome location. For more information about the types of relationships between the Application Server and Message Queue, see The JMS Provider.

When executing Message Queue scripts such as as-install/imq/bin/imqusermgr, use the -varhome option to point the scripts to the Message Queue data if the relationship type is LOCAL or EMBEDDED. For example:

imqusermgr -varhome $AS_INSTALL/domains/domain1/imq add -u testuser

For more information about the Message Queue software, refer to the documentation at http://docs.sun.com/app/docs/coll/1343.3.

Delivering SOAP Messages Using the JMS API

Web service clients use the Simple Object Access Protocol (SOAP) to communicate with web services. SOAP uses a combination of XML-based data structuring and Hyper Text Transfer Protocol (HTTP) to define a standardized way of invoking methods in objects distributed in diverse operating environments across the Internet.

For more information about SOAP, see the Apache SOAP web site at http://xml.apache.org/soap/index.html.

You can take advantage of the JMS provider’s reliable messaging when delivering SOAP messages. You can convert a SOAP message into a JMS message, send the JMS message, then convert the JMS message back into a SOAP message. The following sections explain how to do these conversions:

ProcedureTo send SOAP messages using the JMS API

  1. Import the MessageTransformer library.

    import com.sun.messaging.xml.MessageTransformer;

    This is the utility whose methods you use to convert SOAP messages to JMS messages and the reverse. You can then send a JMS message containing a SOAP payload as if it were a normal JMS message.

  2. Initialize the TopicConnectionFactory, TopicConnection, TopicSession, and publisher.

    tcf = new TopicConnectionFactory();
    tc = tcf.createTopicConnection();
    session = tc.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);
    topic = session.createTopic(topicName);
    publisher = session.createPublisher(topic);
  3. Construct a SOAP message using the SOAP with Attachments API for Java (SAAJ).

    *construct a default soap MessageFactory */
    MessageFactory mf = MessageFactory.newInstance();
    * Create a SOAP message object.*/
    SOAPMessage soapMessage = mf.createMessage();
    /** Get SOAP part.*/
    SOAPPart soapPart = soapMessage.getSOAPPart();
    /* Get SOAP envelope. */
    SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
    /* Get SOAP body.*/
    SOAPBody soapBody = soapEnvelope.getBody();
    /* Create a name object. with name space */
    /* http://www.sun.com/imq. */
    Name name = soapEnvelope.createName("HelloWorld", "hw",
     "http://www.sun.com/imq");
    * Add child element with the above name. */
    SOAPElement element = soapBody.addChildElement(name)
    /* Add another child element.*/
    element.addTextNode( "Welcome to Sun Java System Web Services." );
    /* Create an atachment with activation API.*/
    URL url = new URL ("http://java.sun.com/webservices/");
    DataHandler dh = new DataHandler (url);
    AttachmentPart ap = soapMessage.createAttachmentPart(dh);
    /*set content type/ID. */
    ap.setContentType("text/html");
    ap.setContentId("cid-001");
    /**  add the attachment to the SOAP message.*/
    soapMessage.addAttachmentPart(ap);
    soapMessage.saveChanges();
  4. Convert the SOAP message to a JMS message by calling the MessageTransformer.SOAPMessageintoJMSMessage() method.

    Message m = MessageTransformer.SOAPMessageIntoJMSMessage (soapMessage, 
    session );
  5. Publish the JMS message.

    publisher.publish(m);
  6. Close the JMS connection.

    tc.close();

ProcedureTo receive SOAP messages using the JMS API

  1. Import the MessageTransformer library.

    import com.sun.messaging.xml.MessageTransformer;

    This is the utility whose methods you use to convert SOAP messages to JMS messages and the reverse. The JMS message containing the SOAP payload is received as if it were a normal JMS message.

  2. Initialize the TopicConnectionFactory, TopicConnection, TopicSession, TopicSubscriber, and Topic.

    messageFactory = MessageFactory.newInstance();
    tcf = new com.sun.messaging.TopicConnectionFactory();
    tc = tcf.createTopicConnection();
    session = tc.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
    topic = session.createTopic(topicName);
    subscriber = session.createSubscriber(topic);
    subscriber.setMessageListener(this);
    tc.start();
  3. Use the OnMessage method to receive the message. Use the SOAPMessageFromJMSMessage method to convert the JMS message to a SOAP message.

    public void onMessage (Message message) {
    SOAPMessage soapMessage =
     MessageTransformer.SOAPMessageFromJMSMessage( message,
     messageFactory ); }
  4. Retrieve the content of the SOAP message.

Chapter 15 Using the JavaMail API

This chapter describes how to use the JavaMailTM API, which provides a set of abstract classes defining objects that comprise a mail system.

This chapter contains the following sections:

Introducing JavaMail

The JavaMail API defines classes such as Message, Store, and Transport. The API can be extended and can be subclassed to provide new protocols and to add functionality when necessary. In addition, the API provides concrete subclasses of the abstract classes. These subclasses, including MimeMessage and MimeBodyPart, implement widely used Internet mail protocols and conform to the RFC822 and RFC2045 specifications. The JavaMail API includes support for the IMAP4, POP3, and SMTP protocols.

The JavaMail architectural components are as follows:

For more information, see the Sun Java System Application Server Enterprise Edition 8.1 2005Q2 Administration Guide and the JavaMail specification at http://java.sun.com/products/javamail/.

Creating a JavaMail Session

You can create a JavaMail session in the following ways:

JavaMail Session Properties

You can set properties for a JavaMail Session object. Every property name must start with a mail- prefix. The Application Server changes the dash (-) character to a period (.) in the name of the property and saves the property to the MailConfiguration and JavaMail Session objects. If the name of the property doesn’t start with mail-, the property is ignored.

For example, if you want to define the property mail.from in a JavaMail Session object, first define the property as follows:

After you get the JavaMail Session object, you can get the mail.from property to retrieve the value as follows:

String password = session.getProperty("mail.from");

Looking Up a JavaMail Session

The standard Java Naming and Directory InterfaceTM (JNDI) subcontext for JavaMail sessions is java:comp/env/mail.

Registering JavaMail sessions in the mail naming subcontext of a JNDI namespace, or in one of its child subcontexts, is standard. The JNDI namespace is hierarchical, like a file system’s directory structure, so it is easy to find and nest references. A JavaMail session is bound to a logical JNDI name. The name identifies a subcontext, mail, of the root context, and a logical name. To change the JavaMail session, you can change its entry in the JNDI namespace without having to modify the application.

The resource lookup in the application code looks like this:

InitialContext ic = new InitialContext();
String snName = "java:comp/env/mail/MyMailSession";
Session session = (Session)ic.lookup(snName);

For more information about the JNDI API, see Chapter 13, Using the Java Naming and Directory Interface.

Sending and Reading Messages Using JavaMail

ProcedureTo send a message using JavaMail

  1. Import the packages that you need.

    import java.util.*;
    import javax.activation.*;
    import javax.mail.*;
    import javax.mail.internet.*;
    import javax.naming.*;
  2. Look up the JavaMail session.

    InitialContext ic = new InitialContext();
    String snName = "java:comp/env/mail/MyMailSession";
    Session session = (Session)ic.lookup(snName);

    For more information, see Looking Up a JavaMail Session.

  3. Override the JavaMail session properties if necessary.

    For example:

    Properties props = session.getProperties();
    props.put("mail.from", "user2@mailserver.com");
  4. Create a MimeMessage.

    The msgRecipient, msgSubject, and msgTxt variables in the following example contain input from the user:

    Message msg = new MimeMessage(session);
    msg.setSubject(msgSubject);
    msg.setSentDate(new Date());
    msg.setFrom();
    msg.setRecipients(Message.RecipientType.TO, 
       InternetAddress.parse(msgRecipient, false));
    msg.setText(msgTxt);
  5. Send the message.

    Transport.send(msg);

ProcedureTo read a message using JavaMail

  1. Import the packages that you need.

    import java.util.*;
    import javax.activation.*;
    import javax.mail.*;
    import javax.mail.internet.*;
    import javax.naming.*;
  2. Look up the JavaMail session.

    InitialContext ic = new InitialContext();
    String snName = "java:comp/env/mail/MyMailSession";
    Session session = (javax.mail.Session)ic.lookup(snName);

    For more information, see Looking Up a JavaMail Session.

  3. Override the JavaMail session properties if necessary.

    For example:

    Properties props = session.getProperties();
    props.put("mail.from", "user2@mailserver.com");
  4. Get a Store object from the Session, then connect to the mail server using the Store object’s connect() method.

    You must supply a mail server name, a mail user name, and a password.

    Store store = session.getStore();
    store.connect("MailServer", "MailUser", "secret");
  5. Get the INBOX folder.

    Folder folder = store.getFolder("INBOX");
  6. It is efficient to read the Message objects (which represent messages on the server) into an array.

    Message[] messages = folder.getMessages();

Chapter 16 Using the Java Management Extensions (JMX) API

The Sun JavaTM System Application Server uses Java Management Extensions (JMXTM) technology for monitoring, management and notification purposes. Management and monitoring of the Application Server is performed by the Application Server Management Extensions (AMX), which exposes managed resources for remote management via the JMX Application Programming Interface (API).

The Application Server incorporates the JMX 1.2 Reference Implementation, that was developed by the Java Community Process as Java Specification Request (JSR) 3, and the JMX Remote API 1.0 Reference Implementation (JSR 160).

This chapter assumes some familiarity with the JMX technology, but the AMX interfaces can be used for the most part without understanding JMX.

The JMX specifications and Reference Implementations are available for download at http://java.sun.com/products/JavaManagement/download.html.

This chapter contains the following topics:

About AMX

This section describes the Application Server Management eXtensions (AMX). AMX is an API that exposes all of the Application Server configuration and monitoring MBeans as easy-to-use client-side dynamic proxies implementing the AMX interfaces.

Full API documentation for the AMX API is provided in the following Application Server package:

com.sun.appserv.management

The Application Server is based around the concept of administration domains, which consist of one or more managed resources. A managed resource can be an Application Server instance, a cluster of such instances, or a manageable entity within a server instance. A managed resource is of a particular type, and each resource type exposes a set of attributes and administrative operations that change the resource’s state.

Managed resources are exposed as JMX management beans, or MBeans. While the MBeans can be accessed via standard JMX APIs (for example, MBeanServerConnection), most users find the use of the AMX client-side dynamic proxies much more convenient.

All the vital components of the Application Server are visible for monitoring and management via AMX. You can use third-party tools to perform all common administrative tasks programmatically, based on the JMX and JMX Remote API standards.

The AMX API consists of a set of proxy interfaces. MBeans are registered in the JMX runtime contained in the Domain Administration Server (DAS). AMX provides routines to obtain proxies for MBeans, starting with a root-level domain MBean.

You can navigate generically through the MBean hierarchy using the com.sun.appserv.management.base.Container interface. When using AMX, the interfaces defined are implemented by client-side dynamic proxies, but they also implicitly define the MBeanInfo that is made available by the MBean or MBeans corresponding to it. Certain operations defined in the interface might have a different return type or a slightly different name when accessed through the MBean directly. This results from the fact that direct access to JMX requires the use of ObjectName, whereas use of the AMX interfaces is via strongly typed proxies implementing the interface(s).

AMX MBeans

All AMX MBeans are represented as interfaces in a subpackage of com.sun.appserv.management and are implemented by dynamic proxies on the client-side. While you can access AMX MBeans directly through standard JMX APIs, most users find the use of AMX interface (proxy) classes to be most convenient.

An AMX MBean belongs to an application server domain. There is exactly one domain per DAS. Thus all MBeans accessible through the DAS belong to a single Application Server administrative domain. All MBeans in an Application Server administrative domain, and hence within the DAS, belong to the JMX domain amx. Any MBeans that do not have the JMX domain amx are not part of AMX, and are neither documented nor supported for use by clients. All AMX MBeans can be reached navigationally through the DomainRoot.

AMX defines different types of MBean, namely, configuration MBeans, monitoring MBeans, utility MBeans and J2EE management (JSR 77) MBeans. These MBeans are logically related in the following ways:

Configuration MBeans

Configuration information for a given Application Server domain is stored in a central repository that is shared by all instances in that domain. The central repository can only be written to by the DAS. However, configuration information in the central repository is made available to administration clients via AMX MBeans.

The configuration MBeans are those that modify the underlying domain.xml or related files. Collectively, they form a model representing the configuration and deployment repository and the operations that can be performed on them.

The Group Attribute of configuration MBeans, obtained from getGroup(), has a value of com.sun.appserv.management.base.AMX.GROUP_CONFIGURATION.

Monitoring MBeans

Monitoring MBeans provide transient monitoring information about all the vital components of the Application Server.

The Group Attribute of monitoring MBeans, obtained from getGroup(), has a value of com.sun.appserv.management.base.AMX.GROUP_MONITORING.

Utility MBeans

Utility MBeans provide commonly used services to the Application Server.

The Group Attribute of utility MBeans, obtained from getGroup(), has a value of com.sun.appserv.management.base.AMX.GROUP_UTILITY.

J2EE Management MBeans

The J2EE management MBeans implement, and in some cases extend, the management hierarchy as defined by JSR 77, which specifies the management model for the whole J2EE platform. One of the management APIs implemented in JSR 77 is the JMX API.

The implementation of JSR 77 in AMX offers access to and monitoring of MBeans via J2EE management MBeans, by using the getMonitoringPeer() and getConfigPeer() methods.

The J2EE management MBeans can be thought of as the central hub from which other MBeans are obtained.

The Group Attribute of J2EE management MBeans, obtained from getGroup(), has a value of com.sun.appserv.management.base.AMX.GROUP_JSR77.

Other MBeans

MBeans that do not fit into one of the above four categories have the value com.sun.appserv.management.base.AMX.GROUP_OTHER. One such example is com.sun.appserv.management.deploy.DeploymentMgr.

MBean Notifications

All AMX MBeans that emit Notifications place a java.util.Map within the userData field of a standard Notification, which can be obtained via Notification.getUserData(). Within the map are zero or more items, which vary according to the Notification type. Each Notification type, and the data available within the Notification, is defined in its respective MBean or in an appropriate place.

Note that certain standard Notifications, such as javax.management.AttributeChangeNotification do not and cannot follow this behavior.

Access to MBean Attributes

An AMX MBean Attribute is accessible in three ways:

All dotted names that are accessible via the command line interface are available as Attributes within a single MBean. This includes properties, which are Attributes beginning with the prefix property., for example, server.property.myproperty.


Note –

Certain attributes that may be of a specific type, such as int, are declared as java.lang.String. This is because the value of the attribute may be a template of a form such as ${HTTP_LISTENER_PORT}.



Note –

An application or module MBean has two enabled attributes. Which enabled attribute to look up for the state of the application or module depends on the target. If the target is domain, look up the attribute under application or module itself. If the target is not domain, look up the attribute under the application-ref.

To look up an application in JConsole, select the following:

com.sun.appserv -> j2ee-application -> app-name -> server -> config

To look up a module in JConsole, select the following:

com.sun.appserv -> module-type -> module-name -> server -> config

To look up an application-ref in JConsole, select the following:

com.sun.appserv -> application-ref -> app-or-module-name -> server -> config

The attributes, including enabled, are listed under config.


Proxies

Proxies are an important part of the AMX API, and enhance ease-of-use for the programmer.

While JMX MBeans can be used directly, client-side proxies are offered to facilitate navigation through the MBean hierarchy. In some cases, proxies also function as support or helper objects to simplify the use of the MBeans.

See the API documentation for the com.sun.appserv.management package and its sub-packages for more information about using proxies. The API documentation explains the use of AMX with proxies. If you are using JMX directly (for example, via MBeanServerConnection), the return type, argument types and method names might vary as needed for the difference between a strongly-typed proxy interface and generic MBeanServerConnection/ObjectName interface.

Connecting to the Domain Administration Server

As stated in Configuration MBeans, the AMX API allows client applications to connect to Application Server instances via the DAS. All AMX connections are established to the DAS only: AMX does not support direct connections to individual server instances. This makes it simple to interact with all servers, clusters, and so on, with a single connection.

Sample code for connecting to the DAS is shown in Connecting to the DAS.

Examining AMX Code Samples

The following example uses of AMX are discussed in this document:

Connecting to the DAS

The connection to the DAS is shown in the following code.


Example 16–1 Connecting to the DAS

[...]
public static AppserverConnectionSource
    connect(
        final String host,
        final int port,
        final String user,
        final String password,
        final TLSParams tlsParams )
        throws IOException
        {
            final String info = "host=" + host + ", port=" + port +
                ", user=" + user + ", password=" + password +
                ", tls=" + (tlsParams != null);

            SampleUtil.println( "Connecting...:" + info );

            final AppserverConnectionSource conn    =
                new AppserverConnectionSource(
                    AppserverConnectionSource.PROTOCOL_RMI,
                    host, port, user, password, tlsParams, null);

            conn.getJMXConnector( false );

            SampleUtil.println( "Connected: " + info );

            return( conn );
        }
[...]

A connection to the DAS is obtained via an instance of the com.sun.appserv.management.client.AppserverConnectionSource class. For the connection to be established, you must know the name of the host and port number on which the DAS is running, and have the correct user name, password and TLS parameters.

Once the connection to the DAS is established, DomainRoot is obtained as follows:

DomainRoot domainRoot = appserverConnectionSource.getDomainRoot();

This DomainRoot instance is a client-side dynamic proxy to the MBean amx:j2eeType=X-DomainRoot,name=amx.

See the API documentation for com.sun.appserv.management.client.AppserverConnectionSource for further details about connecting to the DAS using the AppserverConnectionSource class.

However, if you prefer to work with standard JMX, instead of getting DomainRoot, you can get the MBeanServerConnection or JMXConnector, as shown:

MBeanServerConnection conn =
appserverConnectionSource.getMBeanServerConnection( false );
JMXConnector jmxConn =
appserverConnectionSource.getJMXConnector( false );

Starting an Application Server

The startServer() method demonstrates how to start an Application Server.


Example 16–2 Starting an Application Server

[...]
startServer( final String serverName )
{
    final J2EEServer server    = getJ2EEServer( serverName );

    server.start();
}
[...]

This method retrieves and starts an application server instance named server. The server is an instance of the com.sun.appserv.management.j2see.J2EEServer interface, and is obtained by calling another method, getJ2EEServer(), shown in the following code.


Example 16–3 Obtaining a Named J2EE server instance

[...]
getJ2EEServer( final String serverName )
{
    final J2EEDomain j2eeDomain = getDomainRoot().getJ2EEDomain();
    final Map servers = j2eeDomain.getServerMap();
    final J2EEServer server = (J2EEServer)servers.get( serverName );
        if ( server == null )
        {
            throw new IllegalArgumentException( serverName );
        }
    return( server );
}
[...]

To obtain a J2EE server instance, the getJ2EEServer() method first of all obtains an instance of the J2EEDomain interface by calling the com.sun.appserv.management.base.AMX.getDomainRoot() and com.sun.appserv.management.DomainRoot.getJ2EEDomain() methods. The two methods called establish the following:

The J2EEServer instance is then started by a call to the start() method. The com.sun.appserv.management.j2ee.StateManageable.start() method can be used to start any state manageable object.

Deploying an Archive

The uploadArchive() and deploy() methods demonstrate how to upload and deploy a J2EE archive file.


Example 16–4 Uploading an archive

[...]
uploadArchive ( final File archive  ) throws IOException
{
    final FileInputStream input = new FileInputStream( archive );
    final long length = input.available();
    final DeploymentMgr mgr = getDomainRoot().getDeploymentMgr();
    final Object uploadID = mgr.initiateFileUpload( length );
    try
    {
        [...]
    }
    finally
    {
        input.close();
    }
    return( uploadID );
}
[...]

The uploadArchive() method creates a standard Java FileInputStream instance called input, to upload the archive archive. It then obtains the AMX deployment manager running in the application server domain, by calling the DomainRoot.getDeploymentMgr() method.

A call to com.sun.appserv.management.deploy.initiateFileUpload starts the upload of archive. The initiateFileUpload() method automatically issues an upload ID, that uploadArchive() returns when it is called by deploy().


Example 16–5 Deploying an archive

[...]
deploy ( final File archive ) throws IOException
{
    final Object uploadID = uploadArchive(archive);
    final DeploymentMgr mgr    = getDomainRoot().getDeploymentMgr();
    final Object deployID = mgr.initDeploy( );
    final DeployNotificationListener myListener =
        new DeployNotificationListener( deployID);
    mgr.addNotificationListener( myListener, null, null);
    try
    {
        final Map    options  = new HashMap();
        options.put( DeploymentMgr.DEPLOY_OPTION_VERIFY_KEY,
            Boolean.TRUE.toString() );
        options.put( DeploymentMgr.DEPLOY_OPTION_DESCRIPTION_KEY,
            "description" );
        mgr.startDeploy( deployID, uploadID, null, null);
        while ( ! myListener.isCompleted() )
        {
            try
            {
                println( "deploy: waiting for deploy of " + archive);
                Thread.sleep( 1000 );
            }
            catch( InterruptedException e )
            {
            }
        }
        final DeploymentStatus status = myListener.getDeploymentStatus();
        println( "Deployment result: " + getStageStatusString(
            status.getStageStatus() ) );
        if ( status.getStageThrowable() != null )
        {
            status.getStageThrowable().printStackTrace();
        }
    }
    finally
    {
        try
        {
            mgr.removeNotificationListener( myListener );
        }
        catch( Exception e )
        {
        }
    }
}
[...]

The deploy() method calls uploadArchive to get the upload ID for archive. It then identifies the deployment manager by calling DomainRoot.getDeploymentMgr(). A call to DeploymentMgr.initDeploy() initializes the deployment and obtains a deployment ID, which is used to track the progress of the deployment.

A JMX notification listener, myListener, is created and activated to listen for notifications regarding the deployment of deployID.

Deployment is started by calling the DeploymentMgr.startDeploy() method and providing it with the deployID and uploadID.

While the deployment is continuing, myListener listens for the completion notification and DeploymentStatus keeps you informed of the status of the deployment by regularly calling its getStageStatus() method. Once the deployment is complete, the listener is closed down.


Caution – Caution –

Some of the behavior of the com.sun.appserv.management.deploy API is unpredictable, and it should be used with caution.


Displaying the AMX MBean Hierarchy

The displayAMX() method demonstrates how to display the AMX MBean hierarchy.


Example 16–6 Displaying the AMX MBean Hierarchy

[...]
displayAMX(
    final AMX amx,
    final int indentCount )
{
    final String indent = getIndent( indentCount );
    final String j2eeType = amx.getJ2EEType();
    final String name = amx.getName();
    if ( name.equals( AMX.NO_NAME ) )
    {
        println( indent + j2eeType );
    }
    else
    {
        println( indent + j2eeType + "=" + name );
    }
}
private void
displayHierarchy(
    final Collection amxSet,
    final int indentCount )
{
    final Iterator    iter    = amxSet.iterator();
    while ( iter.hasNext() )
    {
        final AMX amx = (AMX)iter.next();
        displayHierarchy( amx, indentCount );
    }
}
public void
displayHierarchy(
    final AMX amx,
    final int    indentCount )
{
    displayAMX( amx, indentCount );
    if ( amx instanceof Container )
    {
        final Map m = ((Container)amx).getMultiContaineeMap( null );
        final Set deferred = new HashSet();
        final Iterator mapsIter = m.values().iterator();
        while ( mapsIter.hasNext() )
        {
            final Map instancesMap = (Map)mapsIter.next();
            final AMX first = (AMX)instancesMap.values().iterator().next();
            if ( first instanceof Container )
            {
                deferred.add( instancesMap );
            }
            else
            {
                displayHierarchy( instancesMap.values(), indentCount + 2);
            }
        }
        // display deferred items
        final Iterator iter = deferred.iterator();
        while ( iter.hasNext() )
        {
            final Map instancesMap = (Map)iter.next();
            displayHierarchy( instancesMap.values(), indentCount + 2);
        }
    }
}
public void displayHierarchy()
{
    displayHierarchy( getDomainRoot(), 0);
}
public void
displayHierarchy( final String j2eeType )
{
    final Set items = getQueryMgr().queryJ2EETypeSet( j2eeType );
    if ( items.size() == 0 )
    {
        println( "No {@link AMX} of j2eeType "
            + SampleUtil.quote( j2eeType ) + " found" );
    }
    else
    {
        displayHierarchy( items, 0);
    }
}
[...]

The displayAMX() method obtains the J2EE type and the name of an AMX MBean by calling AMX.getJ2EEType and AMX.getName respectively.

The displayHierarchy() method defines a standard Java Collection instance, amxSet, which collects instances of AMX MBeans.

To display the hierarchy of MBeans within a particular MBean in the collection, displayHierarchy() checks whether the MBean is an instance of Container. If so, it creates a set of the MBeans it contains by calling the com.sun.appserv.management.base.Container.getMultiContaineeMap() method.

The MBean hierarchy for a particular J2EE type is displayed by calling the com.sun.appserv.management.base.QueryMgr.queryJ2EETypeSet(), and passing the result to displayHierarchy().

To display the entire AMX MBean hierarchy in a domain, displayHierarchy() calls getDomainRoot() to obtain the root AMX MBean in the domain.

Setting Monitoring States

The setMonitoring() method demonstrates how to set monitoring states.


Example 16–7 Setting Monitoring States

[...]
private static final Set LEGAL_MON =
    Collections.unmodifiableSet( SampleUtil.newSet( new String[]
{
    ModuleMonitoringLevelValues.HIGH,
    ModuleMonitoringLevelValues.LOW,
    ModuleMonitoringLevelValues.OFF,
} ));
public void setMonitoring(
    final String configName,
    final String state )
{
    if ( ! LEGAL_MON.contains( state ) )
    {
        throw new IllegalArgumentException( state );
    }
    final ConfigConfig config =
        (ConfigConfig)getDomainConfig().
        getConfigConfigMap().get( configName );
    final ModuleMonitoringLevelsConfig mon =
        config.getMonitoringServiceConfig().
        getModuleMonitoringLevelsConfig();
    mon.setConnectorConnectionPool( state );
    mon.setThreadPool( state );
    mon.setHTTPService( state );
    mon.setJDBCConnectionPool( state );
    mon.setORB( state );
    mon.setTransactionService( state );
    mon.setWebContainer( state );
    mon.setEJBContainer( state );
}
[...]

The AMX API defines three levels of monitoring in com.sun.appserv.management.config.ModuleMonitoringLevelValues, namely, HIGH, LOW, and OFF.

In this example, the configuration element being monitored is named configName. The com.sun.appserv.management.config.ConfigConfig interface is used to configure the config element for configName in the domain.xml file.

An instance of com.sun.appserv.management.config.ModuleMonitoringLevelsConfig is created to configure the module-monitoring-levels element for configName in the domain.xml file.

The ModuleMonitoringLevelsConfig instance created then calls each of its set methods to change their states to state.

The above is performed by running the set-monitoring command when you run SimpleMain, stating the name of the configuration element to be monitored and the monitoring state to one of HIGH, LOW or OFF.

Accessing AMX MBeans

The handleList() method demonstrates how to access many (but not all) configuration elements.


Example 16–8 Accessing AMX MBeans

[...]
handleList()
{
    final DomainConfig dcp = getDomainConfig();
    println( "\n--- Top-level --- \n" );
    displayMap( "ConfigConfig", dcp.getConfigConfigMap() );
    displayMap( "ServerConfig", dcp.getServerConfigMap() );
    displayMap( "StandaloneServerConfig",
        dcp.getStandaloneServerConfigMap() );
    displayMap( "ClusteredServerConfig",
        dcp.getClusteredServerConfigMap() );
    displayMap( "ClusterConfig", dcp.getClusterConfigMap() );
    println( "\n--- DeployedItems --- \n" );
    displayMap( "J2EEApplicationConfig",
        dcp.getJ2EEApplicationConfigMap() );
    displayMap( "EJBModuleConfig",
        dcp.getEJBModuleConfigMap() );
    displayMap( "WebModuleConfig",
        dcp.getWebModuleConfigMap() );
    displayMap( "RARModuleConfig",
        dcp.getRARModuleConfigMap() );
    displayMap( "AppClientModuleConfig",
        dcp.getAppClientModuleConfigMap() );
    displayMap( "LifecycleModuleConfig",
        dcp.getLifecycleModuleConfigMap() );
    println( "\n--- Resources --- \n" );
    displayMap( "CustomResourceConfig",
        dcp.getCustomResourceConfigMap() );
    displayMap( "PersistenceManagerFactoryResourceConfig",
        dcp.getPersistenceManagerFactoryResourceConfigMap() );
    displayMap( "JNDIResourceConfig",
        dcp.getJNDIResourceConfigMap() );
    displayMap( "JMSResourceConfig",
        dcp.getJMSResourceConfigMap() );
    displayMap( "JDBCResourceConfig",
        dcp.getJDBCResourceConfigMap() );
    displayMap( "ConnectorResourceConfig",
        dcp.getConnectorResourceConfigMap() );
    displayMap( "JDBCConnectionPoolConfig",
        dcp.getJDBCConnectionPoolConfigMap() );
    displayMap( "PersistenceManagerFactoryResourceConfig",
        dcp.getPersistenceManagerFactoryResourceConfigMap() );
    displayMap( "ConnectorConnectionPoolConfig",
        dcp.getConnectorConnectionPoolConfigMap() );
    displayMap( "AdminObjectResourceConfig",
        dcp.getAdminObjectResourceConfigMap() );
    displayMap( "ResourceAdapterConfig",
        dcp.getResourceAdapterConfigMap() );
    displayMap( "MailResourceConfig",
        dcp.getMailResourceConfigMap() );
    final ConfigConfig config =
        (ConfigConfig)dcp.getConfigConfigMap().get( "server-config" );
    println( "\n--- HTTPService --- \n" );
    final HTTPServiceConfig httpService = config.getHTTPServiceConfig();
    displayMap( "HTTPListeners",
        httpService.getHTTPListenerConfigMap() );
    displayMap( "VirtualServers",
        httpService.getVirtualServerConfigMap() );
}
[...]

The handleList() method makes use of the displayMap() method, which simply prints out the key value pairs.

The handleList() method identifies the configuration for a domain by calling the DomainRoot.getDomainConfig() method. This DomainConfig instance then calls each of its getXXXMap() methods in turn, to obtain a Map for each type of AMX MBean. The Map returned by each getter is displayed by displayMap().

Similarly, the AMX MBeans representing the http-service element are displayed as Maps by calling the getXXXMap() methods of the com.sun.appserv.management.config.HTTPServiceConfig interface, and passing them to displayMap().

Accessing and Displaying the Attributes of an AMX MBean

The displayAllAttributes() method demonstrates how to access and display the attributes of an AMX MBean.


Example 16–9 Accessing and Displaying the Attributes of an AMX MBean

[...]
displayAllAttributes( final AMX item )
{
    println( "\n--- Attributes for " + item.getJ2EEType() +
        "=" + item.getName() + " ---" );
    final Extra extra = Util.getExtra( item );
    final Map attrs    = extra.getAllAttributes();
    final Iterator iter = attrs.keySet().iterator();
    while ( iter.hasNext() )
    {
        final String name = (String)iter.next();
        final Object value = attrs.get( name );
        println( name + "=" + toString( value ) );
    }
}
public void
displayAllAttributes( final String j2eeType )
{
    final Set items = queryForJ2EEType( j2eeType );
    if ( items.size() == 0 )
    {
        println( "No {@link AMX} of j2eeType "
            + SampleUtil.quote( j2eeType ) + " found" );
    }
    else
    {
        final Iterator iter= items.iterator();
        while ( iter.hasNext() )
        {
            final AMX amx = (AMX)iter.next();
            displayAllAttributes( amx );
            println( "" );
        }
    }
}
[...]

The displayAllAttributes() method calls the AMX.getName() and AMX.getJ2EEType() methods for an AMX MBean and prints the results onscreen. It then gets all the attributes for that MBean by calling com.sun.appserv.management.base.Extra.getAllAttributes() on the Extra instance returned by com.sun.appserv.management.base.Util.getExtra(). This is repeated for every MBean.

The attributes of AMX MBeans of a certain J2EE type can be displayed by specifying the J2EE type when the command is run. In this case, displayAllAttributes() calls queryForJ2EEType(). The queryForJ2EEType() method calls the com.sun.appserv.management.base.QueryManager.queryPropSet() method on the specified J2EE type to identify all elements of that type in the domain.

Listing AMX MBean Properties

The displayAllProperties() demonstrates how to list AMX MBean properties.


Example 16–10 Listing AMX MBean Properties

[...]
getProperties( final PropertiesAccess pa )
{
    final HashMap m = new HashMap();
    final String[] names = pa.getPropertyNames();
    for( int i = 0; i < names.length; ++i )
    {
        m.put( names[ i ], pa.getPropertyValue( names[ i ] ) );
    }
    return( m );
}
public void
displayAllProperties( )
{
    final Iterator iter    = getQueryMgr().queryAllSet().iterator();
    while ( iter.hasNext() )
    {
        final AMX amx = (AMX)iter.next();
        if ( amx instanceof PropertiesAccess )
        {
            final PropertiesAccess pa = (PropertiesAccess)amx;
            final Map    props    = getProperties( pa );
            if ( props.keySet().size() != 0 )
            {
                println( "\nProperties for:
                    " + Util.getObjectName( AMX)pa ) );
                println( SampleUtil.mapToString(getProperties(pa), "\n") );
            }
        }
    }
}
[...]

The displayAllProperties() method uses another Samples method, getProperties(). This method creates an instance of the com.sun.appserv.management.config.PropertiesAccess interface, and calls its getPropertyNames() method to obtain the names of all the properties for a given AMX MBean. For each property name obtained, its corresponding value is obtained by calling PropertiesAccess.getPropertyValue().

The displayAllProperties() method calls the com.sun.appserv.management.base.QueryMgr.queryAllSet() method to obtain a set of all the AMX MBeans present in the domain. All AMX MBeans that have properties obligatorily extend the PropertiesAccess interface. Any MBean found to extend PropertiesAccess is passed to the getProperties() method, and the list of property values returned is printed onscreen.

Querying

The demoQuery() method demonstrates how to issue queries.

The demoQuery() method uses other methods that are defined by Samples, namely displayWild(), and displayJ2EEType(). The displayWild() method is shown in the following code.


Example 16–11 Querying and displaying wild cards

[...]
queryWild(
    final String propertyName,
    final String propertyValue)
{
    final String[] propNames = new String[] { propertyName };
    final String[] propValues = new String[]{ propertyValue };
    final Set amxs = getQueryMgr().queryWildSet( propNames, propValues );
    return( amxs );
}
public Set
displayWild(
    final String propertyName,
    final String propertyValue)
{
    final Set items = queryWild( propertyName, propertyValue );
    println( "\n--- Queried for " + propertyName + "="
        + propertyValue + " ---" );
    final Iterator    iter    = items.iterator();
    while ( iter.hasNext() )
    {
        final AMX    item    = (AMX)iter.next();
        println( "j2eeType=" + item.getJ2EEType() + ",
            " + "name=" + item.getName() );
    }
}
[...]

The displayWild() method calls queryWild(), to obtain all the AMX MBeans that have object names matching propertyName and propertyValue. To do so, queryWild() calls the com.sun.appserv.management.base.QueryMgr.queryWildSet() method. The queryWildSet() method returns the list of AMX MBeans with object names matching the wild card strings.

For each MBean returned, the displayWild() calls AMX.getJ2EEType() to identify its J2EE type, and prints the result onscreen.

In code that is not shown here, the displayJ2EEType() method calls the queryForJ2EEType() method, which was seen in Accessing and Displaying the Attributes of an AMX MBean, to identify MBeans of a certain J2EE type and print their object names onscreen.


Example 16–12 Querying

[...]
demoQuery()
{
    displayWild( AMX.J2EE_TYPE_KEY, "X-*ResourceConfig" );
    displayWild( AMX.J2EE_TYPE_KEY, "X-*ServerConfig" );
    displayJ2EEType( XTypes.SSL_CONFIG );
    displayJ2EEType( XTypes.CLUSTER_CONFIG );
}
[...]

In the demoQuery() method, the displayWild() and displayJ2EEType() methods are called to find the following MBeans:

Monitoring Attribute Changes

The demoJMXMonitor() demonstrates how to monitor attribute changes.


Example 16–13 Monitoring Attribute Changes

[...]
demoJMXMonitor() throws InstanceNotFoundException, IOException
{
    final JMXMonitorMgr mgr = getDomainRoot().getJMXMonitorMgr();
    final String attrName = "SampleString";
    final String attrValue = "hello";
    final SampleListener sampleListener = new SampleListener();
    final MBeanServerConnection conn =
        Util.getExtra( mgr ).getConnectionSource()
        .getExistingMBeanServerConnection();
    conn.addNotificationListener(
        getMBeanServerDelegateObjectName(),
        sampleListener, null, null );
    final Sample sample = (Sample)getDomainRoot()
        .getContainee( XTypes.SAMPLE );
    final String monitorName = "SampleStringMonitor";
    AMXStringMonitor mon = null;
    try
    {
        try { mgr.remove( monitorName ); }
        catch( Exception e ) {}
        mon = mgr.createStringMonitor( monitorName );
        waitMBeanServerNotification( sampleListener,
            MBeanServerNotification.REGISTRATION_NOTIFICATION,
            Util.getObjectName( mon ) );
        sample.addAttribute( attrName, attrValue );
        mon.addNotificationListener( sampleListener, null, null);
        mon.setObservedAttribute( attrName );
        mon.setStringToCompare( attrValue );
        mon.setNotifyDiffer( true );
        mon.setNotifyMatch( true );
        mon.addObservedObject( Util.getObjectName( sample ) );
        final StdAttributesAccess attrs = Util.getExtra( sample);
        attrs.setAttribute( new Attribute(attrName, "goodbye") );
        attrs.setAttribute( new Attribute(attrName, attrValue) );
        sample.removeAttribute( attrName );
        final Map notifs = sampleListener.getNotifsReceived();
        waitNumNotifs( notifs,
            AttributeChangeNotification.ATTRIBUTE_CHANGE, 4 );
    }
    catch( Throwable t )
    {
        t.printStackTrace();
    }
    finally
    {
        try
        {
            mon.removeNotificationListener( sampleListener );
            if ( mon != null )
            {
                mgr.remove( mon.getName() );
                waitMBeanServerNotification( sampleListener,
                    MBeanServerNotification
                    .UNREGISTRATION_NOTIFICATION,
                    Util.getObjectName( mon ) );
            }
            conn.removeNotificationListener(
            getMBeanServerDelegateObjectName(),
            sampleListener );
        }
        catch( ListenerNotFoundException e )
        {
        }
    }
}
[...]

The demoJmx() method demonstrates the implementation of a JMX monitor MBean, that listens for changes in a certain attribute. This is achieved in the following stages:

  1. A com.sun.appserv.management.monitor.JMXMonitorMgr instance is obtained using the DomainRoot.getJMXMonitorMgr() method.

  2. A SampleListener JMX notification listener that is provided in the sample package is instantiated.

  3. A connection to the domain’s MBean server is obtained by calling com.sun.appserv.management.client.ConnectionSource. getExistingMBeanServerConnection() on the JMXMonitorMgr instance’s Extra information.

  4. The SampleListener notification listener is added to the MBean server connection, with an MBean server delegate obtained from getMBeanServerDelegateObject(). The notification listener is now in place on the MBean server connection.

  5. An AMX MBean, sample, of the type SAMPLE is obtained by calling the com.sun.appserv.management.base.Container.getContainee() method on an instance of the Sample interface. The Sample interface defines a basic AMX MBean.

  6. An AMXStringMonitor, an AMX-compatible JMX StringMonitorMBean, is instantiated by calling createStringMonitor on the JMXMonitorMgr instance created above. The AMXStringMonitor instance then calls waitMBeanServerNotification(). The waitMBeanServerNotification() method waits for MBean server notifications of the type REGISTRATION_NOTIFICATION from the SampleListener instance that is listening on the MBean server connection.

  7. An attribute of name attrName and value attrValue is added to the AMX MBean sample.

  8. Various methods of the AMXStringMonitor instance are called, to add a listener, and to set the value to be observed, the object to be observed, and so on.

  9. Access to the sample MBean’s attributes is obtained by passing the sample MBean’s Extra information to an instance of com.sun.appserv.management.base.StdAttributesAccess. The StdAttributesAccess.setAttribute() method is then called to change the values of these attributes.

  10. The AMXStringMonitor then calls the sample notification listener’s getNotifsReceived() method to retrieve the notifications that resulted from the calls to setAttribute() above. The waitNumNotifs() method waits until four ATTRIBUTE_CHANGE notifications have been received before exiting.

  11. The notification listener is then removed and the monitor is closed down.

Undeploying Modules

The undeploy() method demonstrates how to undeploy a module.


Example 16–14 Undeploying Modules

[...]
undeploy ( final String moduleName ) throws IOException
{
    final DeploymentMgr mgr = getDomainRoot().getDeploymentMgr();

    final Map statusData = mgr.undeploy( moduleName, null );
    final DeploymentStatus status =
        DeploymentSupport.mapToDeploymentStatus( statusData );
    println( "Undeployment result: "
        + getStageStatusString(status.getStageStatus()));
    if ( status.getStageThrowable() != null )
    {
        status.getStageThrowable().printStackTrace();
    }
}
[...]

The undeploy() method obtains the DeploymentMgr instance for the domain in the same way that deploy() does so. It then calls the DeploymentMgr.undeploy() method for a named module.

Stopping an Application Server

The stopServer() method demonstrates how to stop an application server. The stopServer() method simply calls the getJ2EEServer() method on a given server instance, and then calls J2EEServer.stop().

Running the AMX Samples

To set up your development environment for using AMX, you must ensure that your Java classpath contains the following Java archive (JAR) files:

Start your Java application in a manner similar to this:

export JAR_PATH=install-dir/lib/
export CP="$JAR_PATH/j2ee.jar:$JAR_PATH/appserv-admin.jar"
java -cp $CP com.mycompany.MyClientMain