« Posts under Java

Using Jboss Datasource files, JBoss 5.1

If you’re using jboss and you’re storing database connection info in a properties file, you might be doing something wrong. Specifically, you’re probably not using the data source files jboss ships with to configure all that plumbing.

What’s a Datasource file?

Simply put, its a file that contains all the connection properties an application needs in order to connect to a database in xml format. Here’s an example:

<datasources>  
	<local-tx-datasource>
        <jndi-name>DefaultDS</jndi-name>
        <connection-url>jdbc:postgres://dbUrl:5432/schema</connection-url>
        <driver-class>org.postgresql.Driver</driver-class>
	    <user-name>username</user-name>  
	    <password>password</password>
		<metadata>
			<type-mapping>PostgreSQL</type-mapping>
		</metadata>	    
	</local-tx-datasource>
</datasources>

So, the “jndi-name” node gives this datasource configuration the jndi name it will be bound to while the server is running. We’ll need this later since we’ll be using jndi to fetch the datasource configuration. The “connection-url” is the jdbc connection string used to connect to the database; the “driver-class” sets the java driver used to load the database connectors; and the username/password nodes are self explanatory. The “metadata” node is optional, but its good to specify which mapping type jboss should use for this datasource; its job is to set which sql dialect constructs are used to map sql and data during an actual jdbc call. This file itself can be named anything, with the only limitation being that it needs to end with “-ds.xml”. The suffix pretty much flags jboss and lets it know to process the contents of the file as a datasource type. This file needs to be in the deploy directory for it to be loaded up.

Great, now we have a datasource file set up, how do we access it?

If you’re using ejb3, your persistence.xml file references the ds configuration by jndi-name. I’m pretty sure the hibernate configuration files do the same. If you’re using jdbc in your own homegrown persistence layer – gah, read on. Remember how we mapped the ds connection info with a jndi-name? We now get to use the InitialContext object to fetch the ds configuration by name via a jndi lookup:

/*
 *	Returns a java.sql.Connection object, given the ds name to lookup
 *
*/
private Connection getDatasourceConnection(String jndiName) throws Exception { 

	// load the driver first
	Class.forName("org.postgresql.Driver"); 
	String dsFile = "java:/DefaultDS"; 

	// sloppy, but just to show it can be done
	if(jndiName != null) {
		dsFile = jndiName;
	}

	InitialContext ic = new InitialContext(); 
	DataSource ds = (DataSource) ic.lookup(dsFile); 

	// returns an object of java.sql.Connection
	return ds.getConnection(); 
}

What’s powerful about this is because you’re using a jndi name to lookup the datasource, you can set up multiple datasources and just call them by name when you need them. Send in the name of your ds config, and you get back a fully loaded java.sql.Connection object, ready to fire up jdbc calls. But.. jdbc was so.. Y2004…

TANGENT!

If you’re using a legacy jdbc system (hard coded connection strings, ugly bundled properties files to hold connection settings, etc), porting your application to use datasource files could prove to be a worthwhile refactor, its so much cleaner. If you’re building an entirely new application though, take a look at ejb3 persistence, hibernate, they’re mature persistence frameworks that do a lot of the heavy lifting for you. There are others but these two stand out.

Ejb3 Basics: Bean Managed Transactions

I’m Lazy, why would I want to do my own transaction management?

While its true that the ejb3 container is usually pretty smart about persisting and handling transactions, its also not as smart as a real human being and probably isn’t able to handle complex database transactions and rollbacks. This is where bean managed transactions come in. By handling your own transactions you can avoid some major pitfalls.

The first problem with Container Managed Transactions (CMT) is there’s a time limit imposed by the container (although it’s a configurable timeout). If you are performing a lengthy task and are persisting the results (like FTPing files over to a third party vendor, persisting the results of a jms queue distribution, or committing across two different data sources – What the heck is a datasource?) then you will want to have control over when the bean commits the transactions. This frees you from a time constraint and also allows you to decide when you want to commit and what rollback strategy you want to use for error management. If you stick to using CMT during these kinds of database transactions, the container will invariably end up flipping out and start throwing all kinds of errors because out of the box the container is usually set to handle short duration transactions (like 5 minutes). Likewise, if a transaction is taking upwards of say, 30 seconds, it might be a sign that the application’s relationship with the database might not be as efficient as possible.

In a perfect world, CMT management streamlines the process for quick and easy transactions like selects, inserts and updates; the type that have all the data ready to insert and don’t have any weird secondary transaction dependencies that need to be completed before committing or inserting. For other weird transaction management handling, user Bean Managed Transactions like this:

/**
 * set up the transaction maangement style for this class
 *
 */
@TransactionManagement(TransactionManagementType.BEAN)
public class MyClass... {


	// set the session context reference, we're going to use it soon
	@Resource private SessionContext sessionContext;

	public void setReminder(ReminderForm reminder) {

		UserTransaction utx = sessionContext.getUserTransaction();  
		try {  

			// begin the transaction
			utx.begin();  

			/**
			 * persist your object
			 * 
			 */
			em.persist(something);

			// attempt to commit the transaction
			utx.commit();

		} catch(Exception e) {

			// something went wrong, lets try to rollback the transaction
			utx.setRollbackOnly();
			log.error("problem with the database transaction: " + 
				e.getMessage());

		}
	}

}

Using those SessionContext objects with this type of construct you can create as many transactions as you want. The container’s transaction management is hands off during the entire body of this class, but you can override individual method with the CMT flag if you must.

Using Jboss System Properties

So if I have a jboss application set up on different environments, is there an easy way for me to load environment specific properties on a per instance basis?

Yes there is, read on.

So normally, in most applications you might end up with some property values you’ll want to override based on the environment. For example, login credentials to some third party service, environment specific file locations, environment specific jnp servers, or properties that flag the application’s mode as test or production environments.

It’s generally a good idea for an application to have properties bundled within the deployable artifact so that during deployment you can avoid running into “property not found” type of exceptions. Defaults are always a good idea, and since a deployable artifact should be completely self contained, bundling properties during a deploy is a naturally good practice. But what if you want to override these properties with an environmentally dependent set of values?

We can use the jboss “conf” directory. Because jboss uses an instance style of configuration, you can stick your properties in the “/jboss-install-dir/server/configured-instance/conf” directory, and access them easily from within your code. In a nutshell you can ask the System.properties object for the URL location of the “conf” directory, then load your properties file into a local Properties object as a resource. If your property file is not in the “conf” directory, you can then choose to load from the bundled properties (deployed with your archive) the regular way.

Lets take a look at some of the properties jboss loads into System.properties – on startup if you have jboss set up to output debugging during boot, you will eventually see a system property dump like this:



06:34:04,070 INFO [ServerInfo] Java version: 1.6.0_0,Sun Microsystems Inc.
06:34:04,071 INFO [ServerInfo] Java Runtime: OpenJDK Runtime Environment (build 1.6.0_0-b14)
06:34:04,072 INFO [ServerInfo] Java VM: OpenJDK Client VM 14.0-b08,Sun Microsystems Inc.
06:34:04,072 INFO [ServerInfo] OS-System: Linux 2.6.29.4-167.fc11.i686.PAE,i386
06:34:04,074 DEBUG [ServerInfo] Full System Properties Dump
06:34:04,075 DEBUG [ServerInfo] java.vendor: Sun Microsystems Inc.
06:34:04,076 DEBUG [ServerInfo] sun.java.launcher: SUN_STANDARD
06:34:04,076 DEBUG [ServerInfo] sun.management.compiler: HotSpot Client Compiler
06:34:04,076 DEBUG [ServerInfo] os.name: Linux
06:34:04,076 DEBUG [ServerInfo] user.name: jboss
06:34:04,076 DEBUG [ServerInfo] jboss.bind.address: 192.168.1.252
06:34:04,076 DEBUG [ServerInfo] jboss.home.dir: /jboss/jboss-5.1.0.GA
06:34:04,076 DEBUG [ServerInfo] jboss.home.url: file:/jboss/jboss-5.1.0.GA/

06:34:04,076 DEBUG [ServerInfo] java.version: 1.6.0_0
06:34:04,076 DEBUG [ServerInfo] jboss.server.home.dir: /jboss/jboss-5.1.0.GA/server/standard
06:34:04,077 DEBUG [ServerInfo] jboss.server.home.url: file:/jboss/jboss-5.1.0.GA/server/standard/
06:34:04,077 DEBUG [ServerInfo] jboss.server.config.url: file:/jboss/jboss-5.1.0.GA/server/standard/conf/

06:34:04,077 DEBUG [ServerInfo] jboss.common.lib.url: file:/jboss/jboss-5.1.0.GA/common/lib/
06:34:04,078 DEBUG [ServerInfo] java.home: /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/jre
06:34:04,078 DEBUG [ServerInfo] java.endorsed.dirs: /jboss/jboss-5.1.0.GA/lib/endorsed*
06:34:04,079 DEBUG [ServerInfo] jboss.lib.url: file:/jboss/jboss-5.1.0.GA/lib/
06:34:04,079 DEBUG [ServerInfo] bind.address: 192.168.1.252
06:34:04,080 DEBUG [ServerInfo] jboss.server.temp.dir: /jboss/jboss-5.1.0.GA/server/standard/tmp
06:34:04,080 DEBUG [ServerInfo] user.home: /home/jboss
06:34:04,080 DEBUG [ServerInfo] jboss.common.base.url: file:/jboss/jboss-5.1.0.GA/common/
06:34:04,080 DEBUG [ServerInfo] jboss.server.log.dir: /jboss/jboss-5.1.0.GA/server/standard/log
06:34:04,081 DEBUG [ServerInfo] java.io.tmpdir: /tmp
06:34:04,081 DEBUG [ServerInfo] jboss.server.data.dir: /jboss/jboss-5.1.0.GA/server/standard/data
06:34:04,081 DEBUG [ServerInfo] java.rmi.server.hostname: 192.168.1.252
06:34:04,082 DEBUG [ServerInfo] user.dir: /jboss/jboss-5.1.0.GA/bin
06:34:04,082 DEBUG [ServerInfo] java.vm.name: OpenJDK Client VM
06:34:04,082 DEBUG [ServerInfo] jboss.server.base.dir: /jboss/jboss-5.1.0.GA/server
06:34:04,082 DEBUG [ServerInfo] jboss.server.base.url: file:/jboss/jboss-5.1.0.GA/server/
06:34:04,082 DEBUG [ServerInfo] file.encoding: UTF-8
06:34:04,082 DEBUG [ServerInfo] jboss.server.name: standard


Note the bolded entries. You can access these properties by saying something like this:

String serverConfUrl = System.getProperty("jboss.server.config.url")

A more complete solution that would check the server conf directory before loading the bundled properties could look something like this:

String propertiesFileName = "application.properties";

Properties props = new Properties();
String path = System.getProperty("jboss.server.config.url")+propertiesFileName;
URL url = new URL(path);

if(new File(url.toURI()).exists()) {
	props.load(url.openStream());
	log.info("loaded application properties from file: " + path);
} else {
	props.load(MyClass.class.getResourceAsStream("/" + propertiesFileName));
	log.info("loaded application properties: /"+propertiesFileName);
}

There you have it. An easy to use environment specific properties definition strategy with bundled fail over defaults. Try saying that three times.