Today's Java EE application deployment is a common task, but not an easy job. If you have ever been involved in deploying a Java EE application to a large enterprise environment, no doubt you have faced a number of challenges before you click the deploy button. For instance, you have to figure out how to configure JMS, data sources, database schemas, data migrations, third-party products like Documentum for web publishing, dependencies between components and their deployment order, and so on. Although most of today's application servers support application deployment through their administrative interfaces, the deployment task is still far from being a one-button action.
In the first few sections of this article, I will discuss some of the challenges of Java EE deployment. Then I will introduce an intelligent rule-based auto-deployer application, and explain how it can significantly reduce the complexity of Java EE system deployment. I will also give a comprehensive example on how to build XML rules using XStream utility library, how to extend and analyze the standard Java EE packaging (EAR), and perform a complex deployment task just by pushing one button.
A Java EE application is packaged as an enterprise application archive file (EAR). Java EE specification defines the format of an EAR file as depicted in Figure 1.

Figure 1. Standard Java EE EAR file structure
A standard EAR file meets the basic requirements for packaging an application as most of web-based JAVA EE applications are composed solely of web and/or EJB applications. However, it lacks the capability of packaging advanced JAVA EE application modules. For example, the following modules are often used in a JAVA EE application deployment, but cannot be declared in a standard EAR file:
JDBC Connection Pool and DataSource objects
JMS ConnectionFactory and Destination objects
JMX MBeans
SQL statements
Other resource files
Most of Java EE applications require Data sources, schema changes, data migrations, and JMS configurations. Today, these components have to be manually configured and deployed via an administration interface provided by the implementation vendor. This is typically the responsibility of the system administrator.
Another challenge to an application deployer is that he has to know the deployment dependencies and follow the exact order to deploy multiple deployments for one application.
A large Java EE application may have complex dependencies on other deployments. For example, corresponding database tables must be created before an application can be deployed; a JDBC data source must be configured ahead of a JMS server. In these situations, the deployer first has to coordinate with the application architect and developers to find out the deployment requirements and dependencies, and then make a detailed deployment plan. This process is not very efficient; we need a better solution.
How can we help a deployer survive these challenges? Is there a way to simplify this complex deployment process? A possible solution is to use the vendor proprietary capability to extend your EAR to be more intelligent. For example, WebLogic Server supports packaging JDBC and JMS modules into an EAR file, and the WebLogic Deployer can deploy your application as well as application-scope JDBC and JMS modules in one action. Isn't that useful? Wait a second, there are still limitations:
Tightly coupled - By doing this, your EAR is dependent on one vendor's application server. Your application has to be packaged according to the vendor's specification. This means if you want your product to be deployed across different application servers (or if your production wants to support multiple application servers), you have to maintain multiple EARs.
Complicated packaging - Since you have to follow specifications of different vendors, the application packaging is going to be very complicated and hard to understand.
Hard to maintain - For one application, you need to maintain different versions of EARs for different application servers, or even different versions of the same applications server.
Not a true one-button deployment. Since this type of deployment leverages vendor-specific tools, it cannot support deployment tasks that are not supported by application server. For example, one application may need to execute SQL statements to build schemas and load reference data or upload configurations to LDAP server to expose its service endpoints.
A practical solution is to make an intelligent XML rule-based auto-deployer by extending the Java EE packaging.
This solution has three main parts:
Tool: deployment XML rule generator using XStream
Packaging: extend EAR packaging to include the rule XML document using Ant
Deployer: EAR analyzer and auto-deployer using Java's Jar Utility API
The suggested deployment work flow is illustrated in Figure 2.

Figure 2. Deployment work flow
Let's think about the deployment of a Service Order Processing application to a WebLogic server. Here are the deployment tasks that need to be done:
Configure a JDBC connection pool and data source for manipulating the order processing data.
Execute SQL statements for database objects creation (tables, triggers, and reference data, etc.).
Configure a JMS queue to dispatch service order requests.
Upload system properties (e.g., the URL and JNDI name of the JMS queue for order processing) to a LDAP server.
Finally, deploy this application to an application server
The first step is to generate an XML rule from a plan by the application assembler.
To define a deployment plan, the application assembler discusses the deployment requirements with developers and architects. For the sample service order processing system, a deployment plan is defined below:
DataSource,t3://localhost:7001,NONXA,jdbc/testDS,colin,password,jdbc:oracle:thin:@localhost:1521:localdb,oracle.jdbc.driver.OracleDriver
SQL,t3://localhost:7001,jdbc/testDS,sql/testDS.sql
JMS,t3://localhost:7001,PTP,testJmsServer,testJmsRes,jmsTestConnFactory,jms/conn,testQueue,jms/testQueue
LDAP,ldapread-server.com,489,cn=one_button_deployment,o=system_configuration,ldif/test.ldif
APPLICATION,t3://localhost:7001,SOManager,Release v1.0
After the plan is defined, the application assembler runs the deployment tool application to feed in the plan and generate the XML rule document.
The sample application is shown Figure 3.

Figure 3. Sample deployment tool
|
And the generated XML document of the deployment rule (rule.xml) looks like this:
<?xml version="1.0"?>
<rule>
<modules>
<datasource>
<targetUrl>t3://localhost:7001</targetUrl>
<xaType>NONXA</xaType>
<jndiName>jdbc/testDS</jndiName>
<user>colin</user>
<passWord>ARdHTJSytteIAE</passWord>
<dbUrl>jdbc:oracle:thin:@localhost:1521:localdb</dbUrl>
<driver>oracle.jdbc.driver.OracleDriver</driver>
</datasource>
<sql>
<targetUrl>t3://localhost:7001</targetUrl>
<jndiName>jdbc/testDS</jndiName>
<sqlStatement>sql/testDS.sql</sqlStatement>
</sql>
<jms>
<targetUrl>t3://localhost:7001</targetUrl>
<destType>PTP</destType>
<serverName>testJmsServer</serverName>
<resName>testJmsRes</resName>
<connFactory>jmsTestConnFactory</connFactory>
<facJndiName>jms/conn</facJndiName>
<destName>testQueue</destName>
<destJndiName>jms/testQueue</destJndiName>
</jms>
<ldap>
<host>ldapread-server.com</host>
<port>489</port>
<basedn>cn=one_button_deployment,o=system_configuration</basedn>
<ldif>ldif/test.ldif</ldif>
</ldap>
<application>
<targetUrl>t3://localhost:7001</targetUrl>
<appName>SOManager</appName>
<version>Release v1.0</version>
</application>
</modules>
</rule>
For this sample tool, a Factory design pattern is applied to build deployment module, POJOs, from a deployment rule, after that these POJOs are serialized to XML documents by a RuleGenerator using XStream utility library. The class design is shown in Figure 4.

Figure 4. Class diagram of deployment tool
As shown in Figure 5, the RuleGenerator reads a DeployPlan as a file or input stream and then parses the plan to generate a DeployRule Java object. The DeployRule Java object will be serialized to XML document using XStream utility library.

Figure 5. Sequence diagram of deployment rule generation
The sample code of RuleGenerator is shown below:
package onebuttondeploy.tool;
import java.io.FileWriter;
import java.util.ArrayList;
import onebuttondeploy.Constants;
import onebuttondeploy.rule.ApplicationModuleVO;
import onebuttondeploy.rule.DatasourceModuleVO;
import onebuttondeploy.rule.DeployModule;
import onebuttondeploy.rule.DeployRule;
import onebuttondeploy.rule.DeploymentModuleFactory;
import onebuttondeploy.rule.JmsModuleVO;
import onebuttondeploy.rule.LdapModuleVO;
import onebuttondeploy.rule.SqlModuleVO;
import com.thoughtworks.xstream.XStream;
/**
* @author Colin (Chun) Lu
* @Email colinlucs@gmail.com
*/
public class RuleGenerator {
/*
* Convert deploy plan to XML document
*/
public static String getXmlRule(String plan) throws Exception{
return generateDeployRuleXml(plan2Rule(plan));
}
/*
* Instantiate DeployRule object from deploy plan
*/
private static DeployRule plan2Rule(String plan) throws Exception{
if (plan == null)
return null;
DeployRule rule = new DeployRule();
String[] deployModules = plan.split("\n");
for (int i = 0; i < deployModules.length; i++) {
DeployModule module =
DeploymentModuleFactory.getDeploymenModule(deployModules[i]);
if (module != null)
rule.addModules(module);
}
return rule;
}
/*
* Serialize DeployRule object to XML document usign XStream
*/
private static String generateDeployRuleXml(DeployRule rule) throws Exception{
if (rule == null)
return null;
String xml = "<?xml version=\"1.0\"?> \n";
XStream xstream = new XStream();
xstream.alias("rule", DeployRule.class);
xstream.alias("modules", ArrayList.class);
xstream.alias("datasource", DatasourceModuleVO.class);
xstream.alias("jms", JmsModuleVO.class);
xstream.alias("sql", SqlModuleVO.class);
xstream.alias("ldap", LdapModuleVO.class);
xstream.alias("application", ApplicationModuleVO.class);
xml += xstream.toXML(rule);
return xml;
}
}
|
After the Application Assembler generates the XML deployment rule, the next step is to package the EAR to include this rule document.
An extended JAVA EE application EAR including the XML rule will have the following structure. An "EXT-INF" directory is defined inside EAR to keep the extended deployment files as depicted in Figure 6.
Deployment XML rule: rule.xml
SQL files: used by SQL deployment modules specified at the rule document
LDIF files: used by LDAP deployment modules to upload/expose system configurations
Other resource files that are specified and used in the rule

Figure 6. Extended Java EE EAR file structure
In this sample, an ant script is used to package the EAR. Once the EAR is packaged, the application assembler will deliver this extended EAR to the Deployer.
<project name="ExtPackaging" default="package" basedir="..">
<property name="ear.dir" value="${basedir}/ear"/>
<property name="ext.dir" value="${basedir}/ext"/>
<property name="ldap.dir" value="${basedir}/ldap"/>
<property name="sql.dir" value="${basedir}/sql"/>
<property name="etc.dir" value="${basedir}/etc"/>
<property name="dist" value="${basedir}/dist"/>
<property name="application.ear" value="SOManager.ear"/>
<mkdir dir="${ear.dir}/EXT-INF"/>
<mkdir dir="${ear.dir}/EXT-INF/sql"/>
<mkdir dir="${ear.dir}/EXT-INF/ldap"/>
<mkdir dir="${ear.dir}/EXT-INF/etc"/>
<target name="package"
description="creates custom java ee archive">
<copy todir="${ear.dir}/EXT-INF" file="${ext.dir}/rule.xml"/>
<copy todir="${ear.dir}/EXT-INF/sql">
<fileset dir="${sql.dir}">
<include name="*.sql"/>
</fileset>
</copy>
<copy todir="{ear.dir}/EXT-INF/ldap">
<fileset dir="${ldap.dir}">
<include name="*.ldif"/>
</fileset>
</copy>
<copy todir="{ear.dir}/EXT-INF/etc">
<fileset dir="${etc.dir}"/>
</copy>
<ear destfile="${dist}/${application.ear}"
appxml="${ear.dir}/META-INF/application.xml"
manifest="${ear.dir}/META-INF/MANIFEST.MF">
<metainf dir="${ear.dir}/META-INF"/>
<fileset dir="${ear.dir}"/>
</ear>
</target>
</project>
This is the last and most exciting part of the deployment: push one button to kick off the auto-deployment. First, let's see how the sample application works (as depicted in Figure 7):
Browse and upload the EAR
Push the Deploy button
Done!

Figure 7. Sample application of auto-deployment
What happened on the backend?
First, The Deployer Java object wires the real deployment provider class, which is implemented for the specific application server. For example, WebLogic, WebSphere, and JBoss.
Then, the Deployer Java object parses the EAR file to retrieve the rule.xml and de-serializes XML to the Rule POJOs.
Finally, the Deployer Java object calls the provider's deploy() method by passing the EAR and the Rule object. The provider object deploys DataSources, JMS, and executes SQL statements, and/or uploads system configuration details to LDAP, etc.
Application Server Independent: an Inversion of Control (IoC) design pattern is used in the design of the Deployer to keep it independent of the application server. Different DeployProvider classes are implemented for major Java EE application servers, such as WebLogic, WebSphere, and JBOSS. The Deployer Java object wires the real provider class at runtime based on the deployment requirement. Since the providers are pluggable, the system can easily support different application servers, and its core part is vender independent.
Rule-based deployment: a standard, vendor-independent XML rule is designed to instruct the deployer for the deployment. The XStream utility library is used to bind the XML document to Rule POJOs. The rule is also easy to extend to support new requirements.
Extended deployments: various deployment module POJOs are designed to extend the deployable modules and tasks. Not only DataSource and JMS can be deployed automatically, SQL execution, LDAP configuration setup can also be supported. Moreover, a new deployment module POJO can be easily designed to applied to the deploy provider class.
EAR Analyzer: uses Java's Jar utility API to parse and analyze the EAR file, and extracts the interested files (i.e., rule.xml, SQL files, LDIF files, etc.) from the EXT-INF/ of the EAR.
The class design is depicted in Figure 8.

Figure 8. Class diagram of the Deployer Application
The deployment flow is shown in Figure 9.

Figure 9. Sequence diagram of the deployment flow
|
The following is the code sample of the Deployer object:
package onebuttondeploy.deployer;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.apache.log4j.Logger;
import com.thoughtworks.xstream.XStream;
import onebuttondeploy.rule.ApplicationModuleVO;
import onebuttondeploy.rule.DatasourceModuleVO;
import onebuttondeploy.rule.DeployRule;
import onebuttondeploy.rule.JmsModuleVO;
import onebuttondeploy.rule.LdapModuleVO;
import onebuttondeploy.rule.SqlModuleVO;
/**
* @author Colin (Chun) Lu
* @Email colinlucs@gmail.com
*/
public class Deployer {
private DeploymentProvider provider;
public static Logger logger = Logger.getLogger(Deployer.class);
public Deployer(DeploymentProvider provider) {
this.provider = provider;
}
/*
* Factory method: instantiate the provider object,
* and wires this object to deployer instance.
*/
public static final Deployer getInstance(String providerClass) throws Exception{
Class c = Class.forName(providerClass);
DeploymentProvider provider = (DeploymentProvider)c.newInstance();
return new Deployer(provider);
}
/*
* Main deploy method: invokes provider's deploy method,
* passing the EAR as byte array
*/
public String deploy(byte[] appArchive) throws Exception{
DeployRule rule = parseXmlRule(appArchive);
String[] moduleResults = provider.deploy(rule, appArchive);
String result="";
for (int i = 0; moduleResults != null
&& i<moduleResults.length; i++) {
if (i == 0)
result = moduleResults[i];
else
result += "\n" + moduleResults[i];
}
return result;
}
/*
* EAR analyzer method:
* 1. extract the xml rule document using JarEntry
* 2. De-serialize XML to DeployRule using XStream
*/
private DeployRule parseXmlRule(byte[] appArchive) throws Exception{
JarInputStream appEar = new JarInputStream(new ByteArrayInputStream(appArchive));
JarEntry entry = null;
StringBuffer strOut = new StringBuffer();
String xml = null;
while ((entry = appEar.getNextJarEntry()) != null) {
String entryName = entry.getName();
logger.debug("entry name="+entryName);
if(entryName.equals("EXT-INF/rule.xml")){
String aux;
BufferedReader br = new BufferedReader(new InputStreamReader(appEar));
while ((aux=br.readLine())!=null)
strOut.append(aux);
xml = strOut.toString();
appEar.close();
br.close();
break;
}
}
logger.debug("xml="+xml);
DeployRule rule = null;
if (xml != null) {
XStream xstream = new XStream(); // With XPP3 lib
xstream.alias("rule", DeployRule.class);
xstream.alias("modules", ArrayList.class);
xstream.alias("datasource", DatasourceModuleVO.class);
xstream.alias("jms", JmsModuleVO.class);
xstream.alias("sql", SqlModuleVO.class);
xstream.alias("ldap", LdapModuleVO.class);
xstream.alias("application", ApplicationModuleVO.class);
rule = (DeployRule)xstream.fromXML(xml);
}
return rule;
}
}
Utilizing this rule-based auto-deployer in your Java EE application deployment task has the following benefits:
Significantly simplified deployment task: as you can see, this auto-deployer application can really achieve the goal of deployment to be a pushing one-button task.
Well-defined deployment flow: the auto-deployer application helps to clearly define the responsibilities of developer, assembler, and deployer. It releases the heavy load of an application deployer by letting an assembler make a well-defined deployment rule, and the deployer doesn't need to know the technical dependencies and requirements of each application. This makes the deployment much more efficient.
Centralized and transacted deployment management: In a very large enterprise environment, applications are usually deployed on to hundreds of different systems. This deployer application provides a good mean of a centralized management. In addition, it can manage deployment as a transacted action by implementing un-deploy/re-deploy methods. Therefore, the deployer can rollback the deployment or switch to different version very easily.
Application server independent: it is happening that a few different vendors' application servers are running together in a very large enterprise environment. Because the core of this auto-deployer application is vendor independent. It is very easy to support heterogeneous application servers by developing different deploy providers and plug in to the application.
This article discussed the challenges in deploying large Java EE applications. And then an intelligent auto-deployer is proposed to make the deployment task easier. This auto-deployer can significantly simplify the deployment task in a large enterprise environment. It also helps the enterprise to maintain a well-defined application deployment flow.
Colin (Chun) Lu is the systems analyst at Telus Mobility. He has been working in J2EE architecture design and development with WebLogic, and WebSphere since 1999. Currently he is working in the area of SOA for Java EE application design and integration. You can reach Colin at colinlucs@gmail.com
Return to ONJava.
Copyright © 2009 O'Reilly Media, Inc.