JBoss 6.x : Retrieve Datasource properties ( username, database name, password) at runtime, using JMX

Introduction

Working with database connections on enterprise application servers like JBoss is in 99.9% very simple and well standardized. After configuring your environment, you can use JPA in order to handle all database interactions. In that way, you do not need to create any database connections or bother what is the name of: database, username/password as well as datasource name. However, there is this 0.1% of cases in which your application is extraordinary (been there ;-) ) and you need to have some more information and control over your database connections. If you do, in this post you will find how to retrieve properties describing predefined Datasources.

If you just need a list of available datasources, without their properties, you can read this post: list available datasources on JBoss 6.

I assume you already know

JBoss6, JMX basics, Datasource configuration on JBoss 6,  JEE concepts.

I am working with:

JBoss-6.1.0.Final, using Windows 8

State of application

Lets assume there are 2 configured datasources on a given JBoss instance: PostgresXADS and DefaultDS.

1) PostgresXADS: datasource with transaction support: XA
The configuration file looks more or less like:

  
<datasources>
  <xa-datasource>
    <jndi-name>PostgresXADS</jndi-name>
    <rar-name>jboss-xa-jdbc.rar</rar-name>
    ...

    <xa-datasource-property name="User">postgres</xa-datasource-property>
    <xa-datasource-property name="Password">admin</xa-datasource-property>
    <xa-datasource-property name="ServerName">localhost</xa-datasource-property>
    <xa-datasource-property name="PortNumber">5432</xa-datasource-property>
    <xa-datasource-property name="DatabaseName">postgresdb</xa-datasource-property>
  </xa-datasource>
</datasources>

2) DefaultDS: datasource with transaction support: Local.
This is the same ds, you will find in the standard JBoss installation. The configuration file looks more or less like:

<datasources> 
  <local-tx-datasource> 
    <jndi-name>DefaultDS</jndi-name> 
    <driver-class>org.hsqldb.jdbcDriver</driver-class> 
    ... 
    <user-name>sa</user-name> 
    <password></password>  
  </local-tx-datasource> 
</datasources>

In next paragraphs, I will show you how to read out  properties from those XML files, without reading them themselves neither using JBoss Admin Console.

How can it be done

In order to get all desired information, we will use Management Beans, which are deployed on JBoss and can be accessed via JMX. In first part I will show you how to obtain those information from JMX-Console. In the second part I will show how to do exact same operations in JAVA code.

JMX-Console-eye view

In order to get information about datasources, I will use ManagedConnectionFactory MBean (what is mbean). If you have your JBoss running – please go to the following address: http://localhost:8080/jmx-console/HtmlAdaptor?action=displayMBeans&filter=jboss.jca:service=ManagedConnectionFactory

You will be presented with a list of MBeans of service ManagedConnectionFactory .

List of MBeans in JMX-Console

List of MBeans in JMX-Console

We are interested in 2 datasources: DefaultDS and PostgresXADS.

1) DefaultDS

After choosing this bean from the list, we are presented with detailed information about MBean (service=ManagedConnectionFactory) , including all its parameters and methods available for invoking via JMX-Console.

JMX Console. MBean View

JMX Console. MBean View

Method, which is particularly useful, is: getManagedConnectionFactoryAttribute. Using it, one can call all public “get” methods of given ManagedConnectionFactory instance. Since the DefaultDS datasource is local, the concrete implementation of factory is of  type: org.jboss.resource.adapter.jdbc.local.LocalManagedConnectionFactory. Knowing that, you can check which methods of this class you can call. You can read it in java doc or at grepcode. You may also notice that, all “gettable” fields of this class are the same fields which you can set in your *-DS.xml file. For instance, try to enter the parameter “userName” and then click “Invoke” button:

invoge getUserName method

invoke getUserName method

The response should be: “sa“. Since this is what is configured in the Default-DS.xml file. You can try with other parameters like “password”, “connectionURL” etc.

2) PostgresXADS is of transaction type XA and hence that, ManagedConnectionFactoryClass implementation is represented by org.jboss.resource.adapter.jdbc.xa.XAManagedConnectionFactory. The “get” methods which you can call here are different than for Local Datasource, you can find them also in java doc or grepcode.

If you have any XA Datasource configured, you can:
1) go to the MBean view  (go here: http://localhost:8080/jmx-console/HtmlAdaptor?action=displayMBeans&filter=jboss.jca:service=ManagedConnectionFactory and choose XA datasource)
2) go to the row with getManagedConnectionFactoryAttribute
3) enter the parameter “XADataSourceProperties”
4) click the “Invoke” button:

invoke getXADataSourceProperties method

invoke getXADataSourceProperties method

In my case I was presented with a string :

User=postgres
Password=admin
ServerName=localhost
PortNumber=5432
DatabaseName=postgresdb

Which, contains all information written in <xa-datasource-property> tags of PostgresXADS-ds.xml file.

So here we are – using the JMX Console we can read out all information about the DataSources. Obviously, reading it in that way I just shown, doesn’t make much sense, since we can do the same using JBoss Admin tool. However, if we add to that the fact, that we can use MBeans in Java Code and invoke their methods, it will bring us closer to solution of the problem.

Java Code Point of View

Simple code snippet which presents how to read properties of a datasource. I am running this code in managed EJB Stateless Bean.

String TRANSACTION_SUPPORT_LOCAL = "LOCAL";
String TRANSACTION_SUPPORT_XA = "XA";

/** 
* get properties of datasource with given name 
*/
@Override 
public Properties getDatasourceProperties(String dsName) { 

 Properties dsProperties = new Properties(); 

 try { 
 // Get the starting point of the namespace 
  Context ctx = new InitialContext(); 

 // retrieve MBean server 
 MBeanServer server = java.lang.management.ManagementFactory.getPlatformMBeanServer(); 

 // create jmx name for mbean representing ManagedConnectionFactory for given datasource (1)
 ObjectName managedConnectionFactoryForDS = new ObjectName("jboss.jca:name=" + dsName + ",service=ManagedConnectionFactory"); 

 // get the transaction-support property, in order to distinguish 
 // the type of datasource (2)
 Enum transactionSupportEnum = (Enum) server.getAttribute(managedConnectionFactoryForDS, "TransactionSupport"); 
 String transactionSupport = transactionSupportEnum.name(); 

 if (TRANSACTION_SUPPORT_LOCAL.equals(transactionSupport)) { 

  // call the getUserName method from ManagedConnectionFactory 
  String userNamePropertyName = "userName";  
  String userName = invokeManagedConnectionFactoryMethod(server, managedConnectionFactoryForDS, userNamePropertyName);  

  dsProperties.put(userNamePropertyName, userName); 

  // call the getPassword method from ManagedConnectionFactory 
  String passwordPropertyName = "password"; 
  String password = invokeManagedConnectionFactoryMethod(server, managedConnectionFactoryForDS, passwordPropertyName); 

  // to avoid null pointer exception (3)
  password = (password == null) ? "" : password; 

  dsProperties.put(passwordPropertyName, password); 

  // ... read more properties, which you may need 

  } else if (TRANSACTION_SUPPORT_XA.equals(transactionSupport)) { 

  // call the getXADataSourceProperties method from 
  // ManagedConnectionFactory (4) 
  String xAPropertiesName = "XADataSourceProperties"; 
  String dsInfo = invokeManagedConnectionFactoryMethod(server, managedConnectionFactoryForDS, xAPropertiesName); 

  // load the properties into Properties object (5) 
  dsProperties.load(new StringReader(dsInfo)); 

  } else { 
  // log error and throw exception 
  } 

 } catch (Exception e) { 
 // log exception 
 } 

 return dsProperties; 
} 

/** 
 * Invoke "getManagedConnectionFactoryAttribute" method from managed 
 * connection factory, described by given object-name. 
 *  
 * @param managedConnectionFactoryForDS 
 * ObjectName representing JMX name of given factory 
 * @param propertyToRead 
 * name of the property, which will be read out from the factory 
 * @return value of the asked attribute 
*/ 

 private String invokeManagedConnectionFactoryMethod(MBeanServer server, ObjectName managedConnectionFactoryForDS, String propertyToRead) throws ReflectionException, InstanceNotFoundException, MBeanException { 

  // declare type of the argument
  String[] sig = { "string" }; 

  //Declare the argument
  Object[] opArgs1 = { propertyToRead };   

  //call the method
  String value = (String) server.invoke(managedConnectionFactoryForDS, "getManagedConnectionFactoryAttribute", opArgs1, sig); 

 return value; 
}

1) We are creating object which represents JMX Name “jboss.jca:service=ManagedConnectionFactory,name={dsName}” . This is the same name we can use in JMX-Console in order to retrieve concrete mbean, like we did here.

2) The TransactionSupport attribute can be either “XA” or “LOCAL”

3) In our case the password is not set, since Properties object doesn’t allow to put null values, we need to do the conversion. In real live examples that will not happen.

4) Calling this method we will return same string as presented here:

5) Since the string is already in “properties” format, it is easy to load it into Properties file.

Summary

The presented code is very simple  and may not need so much of explanation, but I think it is good to know how it works in the background and also how to check the results on JMX Console which can spare some time and avoid unexpected results.

If you are going to migrate to JBoss 7 soon, you must take into account, that it will not work there, since the whole server configuration is different and also provided Management Beans are different. If you would like to see how to do the same operation on JBoss 7,  you can read it in my other blog post which will come out this month.

Useful Links

http://docs.oracle.com/javase/tutorial/jmx/mbeans/

http://en.wikipedia.org/wiki/Java_Management_Extensions#

http://docs.oracle.com/javase/tutorial/jmx/

http://www.oracle.com/technetwork/java/javase/tech/best-practices-jsp-136021.html)

Advertisements

One Response to JBoss 6.x : Retrieve Datasource properties ( username, database name, password) at runtime, using JMX

  1. Pingback: JBoss 7.x : Retrieve Datasource properties ( username, database name, password) at runtime, using JMX | My Fascinations

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: