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.2 Administration Guide.
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:
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.
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 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.2 Release Notes. For configurations of supported and other drivers, see Configurations for Specific JDBC Drivers.
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.
To integrate the JDBC driver into a Application Server domain, copy the JAR files into the domain-dir/lib directory, then restart the server. This makes classes accessible to any application or module across the domain. For more information , see Using the Common Classloader.
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:
In the Administration Console, open the Resources component, open the JDBC component, and select Connection Pools. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
Use the asadmin create-jdbc-connection-pool command. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
You can test a JDBC connection pool for usability in one of these ways:
In the Administration Console, open the Resources component, open the JDBC component, select Connection Pools, and select the connection pool you want to test. Then select the Ping button in the top right corner of the page. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
Use the asadmin ping-connection-pool command. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual
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.2 Performance Tuning Guide.
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:
In the Administration Console, open the Resources component, open the JDBC component, and select JDBC Resources. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
Use the asadmin create-jdbc-resource command. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
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:
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.2 Administration Guide.
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.jdbc.DataSource ds = (com.sun.appserv.jdbc.DataSource) ctx.lookup("jdbc/MyBase"); Connection con = ds.getConnection(); Connection drivercon = ds.getConnection(con); // Do db operations. con.close();
If you get a Connection from an Application Server JDBC connection pool, create a Statement object, and then use the Statement.getConnection method, the statement returns the physical connection instead of the wrapped connection. When you close this physical connection, you break the connection pool logic. To avoid this problem, use the following asadmin create-jvm-options command, then restart the server:
asadmin create-jvm-options -Dcom.sun.appserv.jdbc.wrapJdbcObjects=true |
For more information about the asadmin create-jvm-options command, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
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.
For general information about transactions, see Chapter 12, Using the Transaction Serviceand the Sun Java System Application Server Enterprise Edition 8.2 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.
Applications that change the isolation level on a pooled connection programmatically risk polluting the pool, which can lead to errors.
Application Server 8.2 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.2 and are found to be J2EE compatible. They are also supported for CMP.
Sun Java System JDBC Driver for Oracle 8i, 9i, and 10g Databases
Sun Java System JDBC Driver for Microsoft SQL Server Databases
For an up to date list of currently supported JDBC drivers, see the Sun Java System Application Server Enterprise Edition 8.2 Release Notes.
Other JDBC drivers can be used with Application Server 8.2, 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.2.
Oracle Thin Type 4 Driver for Oracle 8i, 9i, and 10g Databases
OCI Oracle Type 2 Driver for Oracle 8i, 9i, and 10g Databases
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.2 Administration Guide.
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.
The Java DB database is based on the Derby database from Apache. The Java DB JDBC driver is included with the Application Server by default. This configuration applies to CloudScape databases as well.
The JAR file for the Java DB driver is derbyclient.jar.
Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: JavaDB
DataSource Classname: Specify one of the following:
org.apache.derby.jdbc.ClientDataSource org.apache.derby.jdbc.ClientXADataSource
Properties:
user - Specify the database user.
This is only necessary if Java DB is configured to use authentication. Java DB does not use authentication by default. When the user is provided, it is the name of the schema where the tables reside.
password - Specify the database password.
This is only necessary if Java DB is configured to use authentication.
databaseName - Specify the name of the database.
serverName - Specify the host name or IP address of the database server.
portNumber - Specify the port number of the database server if it is different from the default.
URL: jdbc:derby://serverName:portNumber/databaseName;create=true
Include the ;create=true part only if you want the database to be created if it does not exist.
The JAR files for this driver are smbase.jar, smdb2.jar, and smutil.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: DB2
DataSource Classname: com.sun.sql.jdbcx.db2.DB2DataSource
Properties:
serverName - Specify the host name or IP address of the database server.
portNumber - Specify the port number of the database server.
databaseName - Set as appropriate.
user - Set as appropriate.
password - Set as appropriate.
URL: jdbc:sun:db2://serverName:portNumber;databaseName=databaseName
The JAR files for this driver are smbase.jar, smoracle.jar, and smutil.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: Oracle
DataSource Classname: com.sun.sql.jdbcx.oracle.OracleDataSource
Properties:
serverName - Specify the host name or IP address of the database server.
portNumber - Specify the port number of the database server.
SID - Set as appropriate.
user - Set as appropriate.
password - Set as appropriate.
URL: jdbc:sun:oracle://serverName[:portNumber][;SID=databaseName]
The JAR files for this driver are smbase.jar, smsqlserver.jar, and smutil.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: mssql
DataSource Classname: com.sun.sql.jdbcx.sqlserver.SQLServerDataSource
Properties:
serverName - Specify the host name or IP address and the port of the database server.
portNumber - Specify the port number of the database server.
user - Set as appropriate.
password - Set as appropriate.
selectMethod - Set to cursor.
URL: jdbc:sun:sqlserver://serverName[:portNumber]
The JAR files for this driver are smbase.jar, smsybase.jar, and smutil.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: Sybase
DataSource Classname: com.sun.sql.jdbcx.sybase.SybaseDataSource
Properties:
serverName - Specify the host name or IP address of the database server.
portNumber - Specify the port number of the database server.
databaseName - Set as appropriate. This is optional.
user - Set as appropriate.
password - Set as appropriate.
URL: jdbc:sun:sybase://serverName[:portNumber]
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:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: DB2
DataSource Classname: com.ibm.db2.jcc.DB2SimpleDataSource
Properties:
user - Set as appropriate.
password - Set as appropriate.
databaseName - Set as appropriate.
driverType - Set to 2.
deferPrepares - Set to false.
The JAR file for the Sybase driver is jconn2.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: Sybase
DataSource Classname: Specify one of the following:
com.sybase.jdbc2.jdbc.SybDataSource com.sybase.jdbc2.jdbc.SybXADataSource
Properties:
serverName - Specify the host name or IP address of the database server.
portNumber - Specify the port number of the database server.
user - Set as appropriate.
password - Set as appropriate.
databaseName - Set as appropriate. Do not specify the complete URL, only the database name.
BE_AS_JDBC_COMPLIANT_AS_POSSIBLE - Set to true.
FAKE_METADATA - Set to true.
The JAR file for the MySQL driver is mysql-connector-java-version-bin-g.jar, for example, mysql-connector-java-3.1.12-bin-g.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: mysql
DataSource Classname: Specify one of the following:
com.mysql.jdbc.jdbc2.optional.MysqlDataSource
Properties:
serverName - Specify the host name or IP address of the database server.
port - Specify the port number of the database server.
user - Set as appropriate.
password - Set as appropriate.
databaseName - Set as appropriate.
URL - If you are using global transactions, you can set this property instead of serverName, port, and databaseName.
The MM MySQL Type 4 driver doesn’t provide a method to set the required relaxAutoCommit property, so you must set it indirectly by setting the URL property:
jdbc:mysql://host:port/database?relaxAutoCommit="true"
The JAR file for the MySQL driver is mysql-connector-java-version-bin-g.jar, for example, mysql-connector-java-3.1.12-bin-g.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: mysql
DataSource Classname: Specify one of the following:
com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
Properties:
serverName - Specify the host name or IP address of the database server.
port - Specify the port number of the database server.
user - Set as appropriate.
password - Set as appropriate.
databaseName - Set as appropriate.
URL - If you are using global transactions, you can set this property instead of serverName, port, and databaseName.
The MM MySQL Type 4 driver doesn’t provide a method to set the required relaxAutoCommit property, so you must set it indirectly by setting the URL property:
jdbc:mysql://host:port/database?relaxAutoCommit="true"
The JAR file for the Inet Oracle driver is Oranxo.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: Oracle
DataSource Classname: com.inet.ora.OraDataSource
Properties:
user - Specify the database user.
password - Specify the database password.
serviceName - Specify the URL of the database. The syntax is as follows:
jdbc:inetora:server:port:dbname
For example:
jdbc:inetora:localhost:1521:payrolldb
In this example,localhost is the host name of the machine running the Oracle server, 1521 is the Oracle server’s port number, and payrolldb is the SID of the database. For more information about the syntax of the database URL, see the Oracle documentation.
serverName - Specify the host name or IP address of the database server.
port - Specify the port number of the database server.
streamstolob - If the size of BLOB or CLOB data types exceeds 4 KB and this driver is used for CMP, this property must be set to true.
xa-driver-does-not-support-non-tx-operations - Set to the value true. Optional: only needed if both non-XA and XA connections are retrieved from the same connection pool. Might degrade performance.
As an alternative to setting this property, you can create two connection pools, one for non-XA connections and one for XA connections.
The JAR file for the Inet Microsoft SQL Server driver is Merlia.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: mssql
DataSource Classname: com.inet.tds.TdsDataSource
Properties:
serverName - Specify the host name or IP address and the port of the database server.
port - Specify the port number of the database server.
user - Set as appropriate.
password - Set as appropriate.
The JAR file for the Inet Sybase driver is Sybelux.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: Sybase
DataSource Classname: com.inet.syb.SybDataSource
Properties:
serverName - Specify the host name or IP address of the database server.
portNumber - Specify the port number of the database server.
user - Set as appropriate.
password - Set as appropriate.
databaseName - Set as appropriate. Do not specify the complete URL, only the database name.
The JAR file for the Oracle driver is ojdbc14.jar. Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: Oracle
DataSource Classname: Specify one of the following:
oracle.jdbc.pool.OracleDataSource oracle.jdbc.xa.client.OracleXADataSource
Properties:
user - Set as appropriate.
password - Set as appropriate.
URL - Specify the complete database URL using the following syntax:
jdbc:oracle:thin:[user/password]@host[:port]/service
For example:
jdbc:oracle:thin:@localhost:1521:customer_db
xa-driver-does-not-support-non-tx-operations - Set to the value true. Optional: only needed if both non-XA and XA connections are retrieved from the same connection pool. Might degrade performance.
As an alternative to setting this property, you can create two connection pools, one for non-XA connections and one for XA connections.
You must set the oracle-xa-recovery-workaround property in the Transaction Service for recovery of global transactions to work correctly. For details, see Transaction Scope.
When using this driver, it is not possible to insert more than 2000 bytes of data into a column. To circumvent this problem, use the OCI driver (JDBC type 2).
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:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: Oracle
DataSource Classname: Specify one of the following:
oracle.jdbc.pool.OracleDataSource oracle.jdbc.xa.client.OracleXADataSource
Properties:
user - Set as appropriate.
password - Set as appropriate.
URL - Specify the complete database URL using the following syntax:
jdbc:oracle:oci:[user/password]@host[:port]/service
For example:
jdbc:oracle:oci:@localhost:1521:customer_db
xa-driver-does-not-support-non-tx-operations - Set to the value true. Optional: only needed if both non-XA and XA connections are retrieved from the same connection pool. Might degrade performance.
As an alternative to setting this property, you can create two connection pools, one for non-XA connections and one for XA connections.
Configure the connection pool using the following settings:
Name: Use this name when you configure the JDBC resource later.
Resource Type: Specify the appropriate value.
Database Vendor: Informix
DataSource Classname: Specify one of the following:
com.informix.jdbcx.IfxDataSource com.informix.jdbcx.IfxXADataSource
Properties:
serverName - Specify the Informix database server name.
portNumber - Specify the port number of the database server.
user - Set as appropriate.
password - Set as appropriate.
databaseName - Set as appropriate. This is optional.
IfxIFXHost - Specify the host name or IP address of the database server.
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.2 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.
There are three types of transaction resource managers:
Databases - Use of transactions prevents databases from being left in inconsistent states due to incomplete updates. For information about JDBC transaction isolation levels, see Using JDBC Transaction Isolation Levels.
The Application Server supports a variety of JDBCTM XA drivers. For a list of the JDBC drivers currently supported by the Application Server, see the Sun Java System Application Server Enterprise Edition 8.2 Release Notes. For configurations of supported and other drivers, see Configurations for Specific JDBC Drivers.
Java Message Service (JMS) Providers - Use of transactions ensures that messages are reliably delivered. The Application Server is integrated with Sun Java System Message Queue, a fully capable JMS provider. For more information about transactions and the JMS API, see Chapter 14, Using the Java Message Service.
J2EETM Connector Architecture (CA) components - Use of transactions prevents legacy EIS systems from being left in inconsistent states due to incomplete updates. For more information about connectors, see Chapter 9, Developing Connectors.
For details about how transaction resource managers, the transaction service, and applications interact, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
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.
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:
In the JDBC connection pool configuration, the DataSource class does not implement the javax.sql.XADataSource interface.
The Global Transaction Support box is not checked, or the Resource Type setting does not exist or is not set to javax.sql.XADataSource.
A transaction remains local if the following conditions remain true:
One and only one non-XA resource is used. If any additional non-XA resource is used, the transaction is aborted.
No transaction importing or exporting occurs.
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.
Oracle thin driver - The XAResource.recover method repeatedly returns the same set of in-doubt Xids regardless of the input flag. According to the XA specifications, the Transaction Manager initially calls this method with TMSTARTSCAN and then with TMNOFLAGS repeatedly until no Xids are returned. The XAResource.commit method also has some issues.
To disable the Application Server workaround, set the oracle-xa-recovery-workaround property value to false. For details about how to set this property, see Configuring the Transaction Service.
These workarounds do not imply support for any particular JDBC driver.
You can configure the transaction service in the Application Server in the following ways:
To configure the transaction service using the Administration Console, open the Transaction Service component under the relevant configuration. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
To configure the transaction service, use the asadmin set command to set the following attributes:
server.transaction-service.automatic-recovery = false server.transaction-service.heuristic-decision = rollback server.transaction-service.keypoint-interval = 2048 server.transaction-service.retry-timeout-in-seconds = 600 server.transaction-service.timeout-in-seconds = 0 server.transaction-service.tx-log-dir = domain-dir/logs
You can also set these properties:
server.transaction-service.property.oracle-xa-recovery-workaround = false server.transaction-service.property.disable-distributed-transaction-logging = false server.transaction-service.property.xaresource-txn-timeout = 600 server.transaction-service.property.pending-txn-cleanup-interval = 60 server.transaction-service.property.use-last-agent-optimization = true
You can use the asadmin get command to list all the transaction service attributes and properties. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
The transaction service writes transactional activity into transaction logs so that transactions can be recovered. You can control transaction logging in these ways:
Set the location of the transaction log files using the Transaction Log Location setting in the Administration Console, or set the tx-log-dir attribute using the asadmin set command.
Turn off transaction logging by setting the disable-distributed-transaction-logging property to true. Do this only if performance is more important than transaction recovery.
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:
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:
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.
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 |
---|---|---|
javax.sql.DataSource |
java:comp/env/jdbc |
|
javax.transaction.UserTransaction |
java:comp/UserTransaction |
|
javax.jms.TopicConnectionFactory javax.jms.QueueConnectionFactory |
java:comp/env/jms |
|
javax.mail.Session |
java:comp/env/mail |
|
java.net.URL |
java:comp/env/url |
|
javax.resource.cci.ConnectionFactory |
java:comp/env/eis |
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
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.
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.
The Application Server exposes the following special resources in the naming environment. Full administration details are provided in the following sections:
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:
To create an external JNDI resource using the Administration Console, open the Resources component, open the JNDI component, and select External Resources. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
To create an external JNDI resource, use the asadmin create-jndi-resource command. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
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:
To create a custom resource using the Administration Console, open the Resources component, open the JNDI component, and select Custom Resources. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
To create a custom resource, use the asadmin create-custom-resource command. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
The following XML elements map JNDI names configured in the Application Server to resource references in application client, EJB, and web application components:
resource-env-ref - Maps the resource-env-ref element in the corresponding J2EE XML file to the absolute JNDI name configured in the Application Server.
resource-ref - Maps the resource-ref element in the corresponding J2EE XML file to the absolute JNDI name configured in the Application Server.
ejb-ref - Maps the ejb-ref element in the corresponding J2EE XML file to the absolute JNDI name configured in the Application Server.
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.
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.2 Administration Guide.
This chapter contains the following sections:
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:
If the type is LOCAL, the Message Queue broker starts when the Application Server starts. This is the default for a stand-alone Application Server instance.
To create a 1:1 relationship between Application Server instances and Message Queue brokers, set the type to LOCAL and give each Application Server instance a different default JMS host. You can do this regardless of whether clusters are defined in the Application Server or the Message Queue software.
If the type is REMOTE, the Message Queue broker must be started separately. This is the default if clusters are defined in the Application Server. For information about starting the broker, see the Sun Java System Message Queue 3.7 UR1 Administration Guide.
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.2.
For general information about the JMS API, see the JMS web page at http://java.sun.com/products/jms/index.html.
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:
Connection Factory: A connector connection pool with a max-pool-size of 250 and a corresponding connector resource.
Destination (Topic or Queue): A connector administered object.
You use connector configuration tools to manage JMS resources. For more information, see Chapter 9, Developing Connectors.
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.2 Administration Guide and the Sun Java System Message Queue 3.7 UR1 Administration Guide.
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:
To edit the JMS Service configuration using the Administration Console, open the Java Message Service component under the relevant configuration. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
To configure the JMS service, use the asadmin set command to set the following attributes:
server.jms-service.init-timeout-in-seconds = 60 server.jms-service.type = LOCAL server.jms-service.start-args = server.jms-service.default-jms-host = default_JMS_host server.jms-service.reconnect-interval-in-seconds = 60 server.jms-service.reconnect-attempts = 3 server.jms-service.reconnect-enabled = true server.jms-service.addresslist-behavior = random server.jms-service.addresslist-iterations = 3 server.jms-service.mq-scheme = mq server.jms-service.mq-service = jms
You can also set these properties:
server.jms-service.property.instance-name = imqbroker server.jms-service.property.instance-name-suffix = server.jms-service.property.append-version = false
You can use the asadmin get command to list all the JMS service attributes and properties. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
You can override the JMS Service configuration using JMS connection factory settings. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
The Application Server instance must be restarted after configuration of the JMS Service.
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.
You can create additional JMS hosts in the following ways:
Use the Administration Console. Open the Java Message Service component under the relevant configuration, then select the JMS Hosts component. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
Use the asadmin create-jms-host command. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
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.2 Reference Manual.
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:
Use the Administration Console. Open the Resources component, open the JMS Resources component, then select Physical Destinations. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
Use the asadmin create-jmsdest command. This command acts on the default JMS host of its target. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
To create a destination resource, see Creating JMS Resources: Destinations and Connection Factories.
You can create two kinds of JMS resources in the Application Server:
Connection Factories: administered objects that implement the ConnectionFactory, QueueConnectionFactory, or TopicConnectionFactory interfaces.
Destination Resources: administered objects that implement the Queue or Topic interfaces.
In either case, the steps for creating a JMS resource are the same. You can create a JMS resource in the following ways:
To create a JMS resource using the Administration Console, open the Resources component, then open the JMS Resources component. Click Connection Factories to create a connection factory, or click Destination Resources to create a queue or topic. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
A JMS resource is a type of connector. To create a JMS resource using the command line, see Deploying and Configuring a Stand-Alone Connector Module.
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.
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.
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.
In the Sun Java System Message Queue software, the AddressList property is called imqAddressList.
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.
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.2 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.
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
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.
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.
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.
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:
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.
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);
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();
Convert the SOAP message to a JMS message by calling the MessageTransformer.SOAPMessageintoJMSMessage() method.
Message m = MessageTransformer.SOAPMessageIntoJMSMessage (soapMessage, session );
Publish the JMS message.
publisher.publish(m);
Close the JMS connection.
tc.close();
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.
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();
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 ); }
Retrieve the content of the SOAP message.
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:
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:
The abstract layer declares classes, interfaces, and abstract methods intended to support mail handling functions that all mail systems support.
The internet implementation layer implements part of the abstract layer using the RFC822 and MIME internet standards.
JavaMail uses the JavaBeans Activation Framework (JAF) to encapsulate message data and to handle commands intended to interact with that data.
For more information, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide and the JavaMail specification at http://java.sun.com/products/javamail/.
You can create a JavaMail session in the following ways:
In the Administration Console, open the Resources component and select JavaMail Sessions. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Administration Guide.
Use the asadmin create-javamail-resource command. For details, see the Sun Java System Application Server Enterprise Edition 8.2 Reference Manual.
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:
Name - mail-from
Value - john.doe@sun.com
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");
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.
Import the packages that you need.
import java.util.*; import javax.activation.*; import javax.mail.*; import javax.mail.internet.*; import javax.naming.*;
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.
Override the JavaMail session properties if necessary.
For example:
Properties props = session.getProperties(); props.put("mail.from", "user2@mailserver.com");
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);
Send the message.
Transport.send(msg);
Import the packages that you need.
import java.util.*; import javax.activation.*; import javax.mail.*; import javax.mail.internet.*; import javax.naming.*;
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.
Override the JavaMail session properties if necessary.
For example:
Properties props = session.getProperties(); props.put("mail.from", "user2@mailserver.com");
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");
Get the INBOX folder.
Folder folder = store.getFolder("INBOX");
It is efficient to read the Message objects (which represent messages on the server) into an array.
Message[] messages = folder.getMessages();
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:
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).
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:
They all implement the com.sun.appserv.management.base.AMX interface.
They all have a j2eeType and name property within their ObjectName (see com.sun.appserv.management.base.XTypes and com.sun.appserv.management.j2ee.J2EETypes for the available values of the j2eeType property).
All MBeans that logically contain other MBeans implement the com.sun.appserv.management.base.Container interface.
JSR 77 MBeans that have a corresponding configuration or monitoring peer expose it via getConfigPeer() or getMonitoringPeer(). However, there are many configuration and monitoring MBeans that do not correspond to JSR 77 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 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 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.
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.
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.
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.
An AMX MBean Attribute is accessible in three ways:
Dotted names via MonitoringDottedNames and ConfigDottedNames
Attributes on MBeans via getAttribute(s) and setAttributes(s) (from the standard JMX API)
Getters/setters within the MBean’s interface class, for example, getPort(), setPort(), and so on.
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.
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}.
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.
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.
The following example uses of AMX are discussed in this document:
The connection to the DAS is shown in the following code.
[...] 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 );
The startServer() method demonstrates how to start 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.
[...] 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:
AMX.getDomainRoot() obtains the Application Server domain to which j2eeDomain belongs.
DomainRoot.getJ2EEDomain() obtains the J2EE domain for j2eeDomain.
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.
The uploadArchive() and deploy() methods demonstrate how to upload and deploy a J2EE archive file.
[...] 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().
[...] 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.
Some of the behavior of the com.sun.appserv.management.deploy API is unpredictable, and it should be used with caution.
The displayAMX() method demonstrates how to display 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.
The setMonitoring() method demonstrates how to set 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.
The handleList() method demonstrates how to access many (but not all) configuration elements.
[...] 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().
The displayAllAttributes() method demonstrates how to access and display 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.
The displayAllProperties() demonstrates how to list 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.
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.
[...] 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.
[...] 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:
J2EE_TYPE_KEY MBeans called ResourceConfig
J2EE_TYPE_KEY MBeans called ServerConfig
All SSL_CONFIG MBeans
All CLUSTER_CONFIG MBeans
The demoJMXMonitor() demonstrates how to monitor 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:
A com.sun.appserv.management.monitor.JMXMonitorMgr instance is obtained using the DomainRoot.getJMXMonitorMgr() method.
A SampleListener JMX notification listener that is provided in the sample package is instantiated.
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.
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.
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.
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.
An attribute of name attrName and value attrValue is added to the AMX MBean sample.
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.
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.
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.
The notification listener is then removed and the monitor is closed down.
The undeploy() method demonstrates how to undeploy a module.
[...] 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.
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().
To set up your development environment for using AMX, you must ensure that your Java classpath contains the following Java archive (JAR) files:
appserv-admin.jar - The JAR file containing the AMX interfaces needed for your client. This file is found in install-dir/lib/. No other classes from this JAR file should be used by your program.
jmxri.jar - The runtime libraries for the JMX Reference Implementation. If you are using JDK 1.5, these are already in the JDK.
jmxremote.jar - The runtime libraries for the JMX Remote API. If you are using JDK 1.5, these are already in the JDK.
j2ee.jar - The runtime libraries for the J2EE Platform. This file is found in install-dir/lib/. This JAR file is needed only if you intend to use any of the J2EE Management Statistic classes (javax.management.j2ee.*).
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