Tuesday, February 11, 2014

Using Python script to deploy Java J2EE apps

When deploy a J2EE app, we usually want to update configuration files such as web.xml, hibernate.cfg.xml, ehcache.xml.. We can use a bash script to do that but Python can do better since it provides XML parser.

To make the script for many environments, we will need a configuration file that contains specific environment's app information.
I prefer to make it in MS .INI file format. We will need some sections like environment, hibernate.cfg.xml, web.xml, ehcache.xml... such as:

 [environment]  
 tomcat-folder = /var/lib/tomcat6  
 tomcat-user = tomcat6  
 [hibernate.cfg.xml]  
 hibernate.connection.url = jdbc:postgresql://dbserver:5432/app  
 hibernate.connection.username = username
 hibernate.connection.password = password  
 [web.xml]  
 data1 = abc  
 Data2 = xyz  
To parse that file, we use ConfigParser:
 import sys, os, datetime, shutil, copy, ConfigParser
 from xml.etree import ElementTree as ET 
 app = sys.argv[1]  
 deployFolder = os.path.dirname(os.path.realpath(__file__)) + '/'  
 config = ConfigParser.ConfigParser()  
 config.optionxform = str #preserve case-sensitive  
 config_file = app + '.cfg'  
 config.read(deployFolder + config_file)  
 tomcatFolder = config.get('environment', 'tomcat-folder')  
 appFolder = tomcatFolder + '/webapps/' + app  

Then parse .xml configuration and update them
 #parse web.xml section into dictionary  
 webXMLdic = dict(config.items('web.xml'))  
 #parse the app web.xml  
 webXMLfile = appFolder + '/WEB-INF/web.xml'  
 webXML = ET.parse(webXMLfile)  
 for param in webXML.getroot().getiterator('context-param'):  
      name = param.find('param-name')  
      value = param.find('param-value')  
      if webXMLdic.has_key(name.text):  
           new_value = webXMLdic.get(name.text)  
           value.text = new_value  
      else:  
           print 'not found value for param ' + name.text + ' in ' + config_file  
 #update the app web.xml  
 with open(webXMLfile, 'w') as f:  
   f.write('<?xml version="1.0" encoding="UTF-8"?>\n' \  
           '<!DOCTYPE web-app \nPUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" \n' \  
     '"http://java.sun.com/dtd/web-app_2_3.dtd">\n')  
   ET.ElementTree(webXML.getroot()).write(f,'utf-8')  
 #parse hibernate.cfg.xml section into dictionary  
 hibernateXMLdic = dict(config.items('hibernate.cfg.xml'))  
 #parse the app hibernate.cfg.xml  
 hibernateXML = ET.parse(hibernateXMLfile)  
 hibernateXMLfile = appFolder + '/WEB-INF/classes/hibernate.cfg.xml'  
 #the XPath selector functionality was not implemented in ElementTree until version 1.3, which ships with Python 2.7, so I use iterator loop to work for lower Python versions  
 for property in hibernateXML.getroot().getiterator('property'):  
      name = property.get('name')  
      if hibernateXMLdic.has_key(name):  
           new_value = hibernateXMLdic.get(name)  
           property.text = new_value  
           print name, new_value  
      if (name == 'hbm2ddl.auto'): #remove update/create db if it's declared  
           hibernateXML.getroot().find('session-factory').remove(property)  
 #update the app hibernate.cfg.xml            
 with open(hibernateXMLfile, 'w') as f:  
   f.write('<!DOCTYPE hibernate-configuration PUBLIC \n' \  
      '"-//Hibernate/Hibernate Configuration DTD 3.0//EN" \n' \  
      '"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">\n')  
   ET.ElementTree(hibernateXML.getroot()).write(f)  

We will need other functions such as extract the war file, copy/backup..., but they are simple things to do with Python.