JBoss 7.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 and will also get idea how to call mbeans methods or get other attributes.

If you just need a list of available datasources, without their properties, you can read this post: list available datasources on JBoss 7. 
If you work on JBoss 6, then follow this article: Datasource Properties on JBoss 6.x

I assume you already know

JBoss7, JMX basics, Datasource configuration on JBoss 7, JConsole  (JConsole intro).

I am working with:

JBoss 7.1.1.Final, using Windows 8

State of application

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

avaliable_datasources

JBoss Admin Console – JNDI tree showing configured datasources

1) PostgresXADS: datasource with transaction support: XA
The section describing this datasource in {config}.xml file (in my case standalone.xml) looks more or less like:

 <xa-datasource jndi-name="java:jboss/datasources/PostgresXADS" pool-name="PostgresXADS" enabled="true" use-ccm="false">
 <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>
 <driver>postgresql</driver>
 <security>
 <user-name>postgres</user-name>
 <password>admin</password>
 </security>
 </xa-datasource>

2) ExampleDS: datasource with transaction support: Local.
This is the same ds, you will find in the standard JBoss installation. The section describing this datasource in {config}.xml (in my case standalone.xml) file looks more or less like:

 <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
 <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</connection-url>
 <driver>h2</driver>
 <security>
 <user-name>sa</user-name>
 <password>sa</password>
 </security>
 </datasource>

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 JConsole. In the second part I will show how to do exact same operations in JAVA code.

JConsole-eye view

In order to get information about datasources, I will use JMX MBeans (what is mbean). Basically we will do simple lookup of mbeans and their properties – you can also obtain those using JConsole.

We will use JMX Names :
1) For Local datasources: “jboss.as:subsystem=datasources,data-source={dsName}”
2) For XA datasources: “jboss.as:subsystem=datasources,xa-data-source={dsName}” and “jboss.as:subsystem=datasources,xa-data-source={dsName},xa-datasource-properties=*”

If you have your JBoss running –  please open JConsole (how to?) and find mbeans representing datasources (MBeans tab–> then choose from the tree: jboss.as–>datasources). Depending on your JBoss datasources configuration, you should see something like:

JMX Console : List of datasources

JMX Console : List of datasources

We are interested in 2 datasources: ExampleDS and PostgresXADS.

1) ExampleDS

After choosing this bean from the tree, and then clicking on the “attributes”, we are presented with detailed information about MBean, including all its parameters. If you choose “operations” you will see all methods available for invoking via JConsole.

examplds_attrs

Attributes of ExampleDS datasource

examplds_operations

Operations of ExampleDS datasource

As you may noticed, all informations are clearly presented in the tree, all connection and security informations can be easily obtained using JConsole. You can also call some methods eg testConnectionPool().  Lets see how it looks for XA datasource.

2) PostgresXADS is of transaction type XA and hence that, the mbean tree looks slightly different. If you navigate to it in JConsole you may see:

postgresxa-Objectnane

PostgresXADS mbean overview

As before for local datasource, you can find all information about it: its own objectName  “jboss.as:subsystem=datasources,data-source=PostgresXADS”, attributes and operations.

As you might have noticed, the tree for XA datasource is extended and has 3 additional leafs : DatabaseName, PortNumber, ServerName. Those are the same fields I have declared in my xml config in tags <xa-datasource-property>. The JMX address for them has additional key-value element which is: xa-datasource-properties={propertyName}”, where propertyName is DatabaseName, PortNumber and ServerName.

objectName_xaprops

XAProperty overview in JConsole

If you choose any of those properties, and then choose the “Attributes” position from the tree, you will see that is has only one attribute representing the value entered in the xml config file.

XA_props_PGXADS

Value of XAProperty in JConsole

3) JConsole Summary

Using the JConsole 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, 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.


/**
*
*/
@Override
public Properties getDatasourceProperties(String dsName) {

  Properties dsProperties = new Properties();
  try {

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

  // create jmx name for local datasources name eg. (1a)
  final ObjectName localDSJMXName = new ObjectName("jboss.as:subsystem=datasources,data-source=" + dsName);

  // create jmx name for XA datasources name eg. (1b)
  // "jboss.as:subsystem=datasources,xa-data-source=PostgresXADS"
  final ObjectName xaDSJMXName = new ObjectName("jboss.as:subsystem=datasources,xa-data-source=" + dsName);

  // check wheter given datasource is LOCAL or XA transaction type. (2)

  // is this datasource registered under local mbean jmx name (3a)
  boolean isLocalDS = server.isRegistered(localDSJMXName);

  // is this datasource registered under XA mbean jmx name (3b)
  boolean isXADDS = server.isRegistered(xaDSJMXName);

  if (isLocalDS) {
    // read and put into properties the "user-name" attribute (4a)
    String userNamePropertyName = "userName";
    readAndSaveAttribute(dsProperties, server, localDSJMXName, userNamePropertyName);

    // read and put into properties "the password" attribute (4b)
    String passwordPropertyName = "password";
    readAndSaveAttribute(dsProperties, server, localDSJMXName, passwordPropertyName);

  } else if (isXADDS) {

    // read and put into properties the "user-name" attribute
    String userNamePropertyName = "userName";
    readAndSaveAttribute(dsProperties, server, xaDSJMXName, userNamePropertyName);

    // read and put into properties the "password" attribute
    String passwordPropertyName = "password";
    readAndSaveAttribute(dsProperties, server, xaDSJMXName, passwordPropertyName);

    // additionally we read out the xa-properties (5)
    String canonicalName = xaDSJMXName.getCanonicalName();
    // create jmx query for xa-properties (6)
    ObjectName jmxQuery = new ObjectName(canonicalName + ",xa-datasource-properties=*");

    // get the results matching given filter.
    final Set xaPropertiesMBeans = server.queryMBeans(jmxQuery, null);

    // iterate over mbeans and retrieve information about
    // xa-properties
    for (final ObjectInstance mBean : xaPropertiesMBeans) {

      ObjectName objectName = mBean.getObjectName();

      // get the name of given xa-property e.g.: ServerName (7)
      String attributeKey = objectName.getKeyProperty("xa-datasource-properties");

      // get the value of given xa-property e.g.: localhost
      Object attributeValue = server.getAttribute(objectName, "value");

      // save the attribute value under correct key
      dsProperties.put(attributeKey, attributeValue);
    }

  } else {
   // datasource is not registered in jmx, or is registered under
   // another name
 }
 } catch (MalformedObjectNameException e) {
   //handle the exception
 } catch (AttributeNotFoundException e) {
  //handle the exception
 } catch (InstanceNotFoundException e) {
  //handle the exception
 } catch (MBeanException e) {
   //handle the exception
 } catch (ReflectionException e) {
   //handle the exception
 }

return dsProperties;
 }

/**
 * read out attribute from the meban represented my given object name
 *
 * @param dsProperties - properties in which method will put read property
 * @param server - reference to mbean server
 * @param dsJMXName - object name from mbean which holds wanted attribute
 * @param propertyName - key under the attribute is hold in the mbean
 * @throws AttributeNotFoundException
 * @throws MBeanException
 * @throws InstanceNotFoundException
 * @throws ReflectionException
 */
private void readAndSaveAttribute(Properties dsProperties, MBeanServer server, final ObjectName dsJMXName,
			String propertyName) throws AttributeNotFoundException, MBeanException, InstanceNotFoundException,
			ReflectionException {
  // read the property
  String userName = (String) server.getAttribute(dsJMXName, propertyName);
  // save the property into properties
  dsProperties.put(propertyName, userName);
}

1a, 1b) We are creating objects which represents JMX Name “jboss.as:subsystem=datasources,(xa-)data-source={dsName}” . This is the same name which is used in JConsole, in order to address concrete mbean, what you can see here.

2)  As we saw in JConsole, XA datasource has some attributes saved in addiferent way, hence that we have to check the type of the datasource and according to that we will read it attributes.

3a 3b) Datasource with given name {dsName} will be registered as “jboss.as:subsystem=datasources,data-source={dsName}” or “jboss.as:subsystem=datasources,xa-data-source={dsName}”. Knowing which JMX name is used to registerer the datasorce, we can recognize what type of datasource it is.

4a, 4b) We read the same values which we saw  here.

5) We read out xa-properties which are not saved as attributes of mbean, but as a sibling mbean values, which we could see here.

6) In our case it would be “jboss.as:subsystem=datasources,xa-data-source=PostgresXADS,xa-datasource-properties=*”.

7) That is the way, to read out values from JMX name. If we would call “objectName.getKeyProperty(“xa-datasource“)” we would get: “PostgresXADS”.

Presented code is showing the simplest example and is written rather to be read clearly than to be optimal. Yoy can refactor it according to your needs, reading out more parameters, or enclosing repeated parts of code to separated methos or outside the “if statement”. You can also call any of the mbean methods which you saw for instance here. How to invoke the methods you can read here/

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 use same code on JBoss 6, 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 6,  you can read it in my other blog post:  Datasource properties on JBoss 6.

Useful Links

Configuring Datasources on JBoss7:  https://docs.jboss.org/author/display/AS7/DataSource+configurationhttp://planet.jboss.org/post/how_to_create_an_manage_datasources_in_as7

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

JMX documentation: http://www.oracle.com/technetwork/java/javase/tech/docs-jsp-135989.html

Some words about MBeans on JBoss:  http://docs.jboss.org/jbossas/docs/Server_Configuration_Guide/4/html/Using_JMX_as_a_Microkernel-JBoss_MBean_Services.html

JConsole: http://docs.oracle.com/javase/7/docs/technotes/guides/management/jconsole.html

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

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

Advertisements

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: