Hudson CI server provides plug-in for automated deployment to Tomcat or JBoss servers. But on my current job we use IBM WebSphere as application server in cluster environment. To implement nightly builds with automated testing we had to figure out way to automate deployment.
General idea and code is based on Luciano Resende's posting.
Usual deployment procedure for IBM WebSphere cluster is:
1.Stop cluster
2.Undeploy application
3.Deploy application and change application parameters like classloaders' order
4.Start cluster
5.As a part of automated testing procedure, we had to wait till start completes and then start test
All these steps could take 10-15 minutes depending on environment, was security enabled or not, etc.
To implement automated deployment for WebSphere we can use wsadmin thin client and Apache ant in conjunction with bash scripts.
In order to use wsadmin remotely, several files need to be copied from IBM Websphere node manager.
Wsadmin script, provided on IBM's site, didn't work for me, so I had to change it slightly.
My version of wsadmin
view sourceprint?
01 #!/bin/bash
02 #set -x
03 # example wsadmin launcher
04 binDir=`dirname "$0"`
05 # WAS_HOME should point to the directory for the thin client
06 WAS_HOME="$binDir"
07 USER_INSTALL_ROOT="$WAS_HOME"
08 # JAVA_HOME should point to where java is installed for the thin client
09 WAS_LOGGING="-Djava.util.logging.manager=com.ibm.ws.bootstrap.WsLogManager -Djava.util.logging.configureByServer=true"
10 if [ -f ${JAVA_HOME}/bin/java ]; then
11 JAVA_EXE="${JAVA_HOME}/bin/java"
12 else
13 JAVA_EXE="${JAVA_HOME}/jre/bin/java"
14 fi
15 CLIENTSOAP=-Dcom.ibm.SOAP.ConfigURL=file:"$USER_INSTALL_ROOT"/properties/soap.client.props
16 CLIENTSAS=-Dcom.ibm.CORBA.ConfigURL=file:"$USER_INSTALL_ROOT"/properties/sas.client.props
17 CLIENTSSL=-Dcom.ibm.SSL.ConfigURL=file:"$USER_INSTALL_ROOT"/properties/ssl.client.props
18 wsadminTraceString=-Dcom.ibm.ws.scripting.traceString=com.ibm.*=all=enabled
19 wsadminTraceFile=-Dcom.ibm.ws.scripting.traceFile="$USER_INSTALL_ROOT"/logs/wsadmin.traceout
20 wsadminValOut=-Dcom.ibm.ws.scripting.validationOutput="$USER_INSTALL_ROOT"/logs/wsadmin.valout
21 # For debugging the utility itself
22 WAS_DEBUG="-Djava.compiler=NONE -Xdebug -Xnoagent"
23 #-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=7777"
24 SHELL=com.ibm.ws.scripting.WasxShell
25 # Parse the input arguments
26 isJavaOption=false
27 nonJavaOptionCount=1
28 for option in "$@" ; do
29 if [ "$option" = "-javaoption" ] ; then
30 isJavaOption=true
31 else
32 if [ "$isJavaOption" = "true" ] ; then
33 javaOption="$javaOption $option"
34 isJavaOption=false
35 else
36 nonJavaOption[$nonJavaOptionCount]="$option"
37 nonJavaOptionCount=$((nonJavaOptionCount+1))
38 fi
39 fi
40 done
41 DELIM=" "
42 C_PATH="$WAS_HOME/com.ibm.ws.admin.client_6.1.0.jar:$WAS_HOME/com.ibm.ws.security.crypto_6.1.0.jar"
43 #Platform specific args...
44 PLATFORM='/bin/uname'
45 case $PLATFORM in
46 AIX
Linux
SunOS
HP-UX)
47 CONSOLE_ENCODING=-Dws.output.encoding=console ;;
48 OS/390)
49 EXTRA_D_ARGS="-Dfile.encoding=ISO8859-1 $DELIM-Djava.ext.dirs="$JAVA_EXT_DIRS""
50 EXTRA_X_ARGS="-Xnoargsconversion" ;;
51 esac
52 # Set java options for performance
53 PLATFORM=`/bin/uname`
54 case $PLATFORM in
55 AIX)
56 PERF_JVM_OPTIONS="-Xms256m -Xmx256m -Xquickstart" ;;
57 Linux)
58 PERF_JVM_OPTIONS="-Xms256m -Xmx256m -Xj9 -Xquickstart" ;;
59 SunOS)
60 PERF_JVM_OPTIONS="-Xms256m -Xmx256m -XX:PermSize=40m" ;;
61 HP-UX)
62 PERF_JVM_OPTIONS="-Xms256m -Xmx256m -XX:PermSize=40m" ;;
63 OS/390)
64 PERF_JVM_OPTIONS="-Xms256m -Xmx256m" ;;
65 esac
66 "$JAVA_EXE" \
67 $EXTRA_X_ARGS \
68 $CONSOLE_ENCODING \
69 $javaOption \
70 $WAS_DEBUG \
71 "$CLIENTSAS" \
72 "$CLIENTSSL" \
73 "$CLIENTSOAP" \
74 ${JAASSOAP:+"$JAASSOAP"} \
75 -Dconfig_consistency_check="$CONFIG_CONSISTENCY_CHECK" \
76 -Dwas.install.root="$WAS_HOME" \
77 -Duser.install.root="$USER_INSTALL_ROOT" \
78 $EXTRA_D_ARGS \
79 $PERF_JVM_OPTIONS \
80 $WAS_LOGGING \
81 $wsadminTraceFile \
82 $wsadminTraceString \
83 $wsadminValOut \
84 $wsadminHost \
85 $wsadminConnType \
86 $wsadminPort \
87 $wsadminLang \
88 -classpath "$C_PATH" \
89 $SHELL "${nonJavaOption[@]}"
90 exit $?
Since we want use in on Hudson, probably on other server and locally, we need Ant script.
It's kinda big and a lot of parameters repeats but it gives idea that's happening.
view sourceprint?
001 <?xml version="1.0"?>
002 <project name="was-integration" basedir=".">
003 <property environment="env"/>
004 <property name="was.python.script" value="./wsIntegration.py"/>
005 <property name="application.name" value="APPLICATION"/>
006 <property name="application.cell" value="CELL"/>
007 <property name="application.cluster" value="CLUSTER"/>
008 <property name="host" value="HOST"/>
009 <property name="port" value="PORT"/>
010 <property name="application.ear" value="PATH_TO_EAR"/>
011 <property name="wsadmin" value="${basedir}/wsadmin.sh"/>
012
013 <target name="clusterState" >
014 <exec dir="." executable="${wsadmin}" outputproperty="currentState">
015 <arg value="-conntype"/>
016 <arg value="SOAP"/>
017 <arg value="-lang"/>
018 <arg value="jython"/>
019 <arg value="-host"/>
020 <arg value="${host}"/>
021 <arg value="-port" />
022 <arg value="${port}"/>
023 <arg value="-f"/>
024 <arg value="${was.python.script}"/>
025 <arg value="clusterState"/>
026 <arg value="${application.cell}"/>
027 <arg value="${application.cluster}"/>
028 </exec>
029 </target>
030
031 <target name="clusterStart" >
032 <exec dir="." executable="${wsadmin}">
033 <arg value="-conntype"/>
034 <arg value="SOAP"/>
035 <arg value="-lang"/>
036 <arg value="jython"/>
037 <arg value="-host"/>
038 <arg value="${host}"/>
039 <arg value="-port" />
040 <arg value="${port}"/>
041 <arg value="-f"/>
042 <arg value="${was.python.script}"/>
043 <arg value="clusterStart"/>
044 <arg value="${application.cell}"/>
045 <arg value="${application.cluster}"/>
046 </exec>
047 </target>
048
049 <target name="clusterStopt" >
050 <exec dir="." executable="${wsadmin}">
051 <arg value="-conntype"/>
052 <arg value="SOAP"/>
053 <arg value="-lang"/>
054 <arg value="jython"/>
055 <arg value="-host"/>
056 <arg value="${host}"/>
057 <arg value="-port" />
058 <arg value="${port}"/>
059 <arg value="-f"/>
060 <arg value="${was.python.script}"/>
061 <arg value="clusterStop"/>
062 <arg value="${application.cell}"/>
063 <arg value="${application.cluster}"/>
064 </exec>
065 </target>
066 <target name="undeployApplication" >
067 <exec dir="." executable="${wsadmin}">
068 <arg value="-conntype"/>
069 <arg value="SOAP"/>
070 <arg value="-lang"/>
071 <arg value="jython"/>
072 <arg value="-host"/>
073 <arg value="${host}"/>
074 <arg value="-port" />
075 <arg value="${port}"/>
076 <arg value="-f"/>
077 <arg value="${was.python.script}"/>
078 <arg value="undeployApplication"/>
079 <arg value="${application.name}"/>
080 </exec>
081 </target>
082
083 <target name="redeployApplication" >
084 <exec dir="." executable="${wsadmin}">
085 <arg value="-conntype"/>
086 <arg value="SOAP"/>
087 <arg value="-lang"/>
088 <arg value="jython"/>
089 <arg value="-host"/>
090 <arg value="${host}"/>
091 <arg value="-port" />
092 <arg value="${port}"/>
093 <arg value="-javaoption" />
094 <arg value="-Dwdm.http.host=${host}" />
095 <arg value="-f"/>
096 <arg value="${was.python.script}"/>
097 <arg value="redeployApplication"/>
098 <arg value="${application.ear}"/>
099 <arg value="${application.cell}"/>
100 <arg value="${application.cluster}"/>
101 <arg value="${application.name}"/>
102 </exec>
103 </target>
104 <target name="fullRedeploy">
105 <antcall target="clusterStopt"/>
106 <exec dir="." executable="./waitForState.sh">
107 <arg value="Cluster State: websphere.cluster.stopped"/>
108 <arg value="${host}"/>
109 <arg value="${port}"/>
110 <arg value="${application.cell}"/>
111 <arg value="${application.cluster}"/>
112 </exec>
113 <antcall target="undeployApplication"/>
114 <antcall target="redeployApplication"/>
115 <antcall target="clusterStart"/>
116 <exec dir="." executable="./waitForState.sh">
117 <arg value="Cluster State: websphere.cluster.running"/>
118 <arg value="${host}"/>
119 <arg value="${port}"/>
120 <arg value="${application.cell}"/>
121 <arg value="${application.cluster}"/>
122 </exec>
123 </target>
124 </project>
Most interesting last part then we stop cluster, wait till it shut downs completely, undeploy application, redeploy application, start cluster and wait will it starts successfully.
We use jython as wsadmin programming language.
Source code for wsIntegration.py
view sourceprint?
01 import sys
02 def clusterStop(cell, cluster):
03 cluster = AdminControl.completeObjectName('cell='+cell+',type=Cluster,name='+cluster+',*')
04 print "Stop Cluster : %s" % ( repr(cluster) )
05 AdminControl.invoke(cluster, 'stop')
06 def clusterStart(cell, cluster):
07 cluster = AdminControl.completeObjectName('cell='+cell+',type=Cluster,name='+cluster+',*')
08 print "Start Cluster : %s" % ( repr(cluster) )
09 AdminControl.invoke(cluster, 'start')
10 def clusterState(cell, cluster):
11 cluster = AdminControl.completeObjectName('cell='+cell+',type=Cluster,name='+cluster+',*')
12 state = AdminControl.getAttribute(cluster, 'state')
13 print "Cluster State: %s" %(state)
14 def appState(app):
15 state = AdminControl.completeObjectName('type=Application,name='+app+',*')
16 print "App State: %s" %(state)
17 def undeployApplication(appName):
18 AdminApp.uninstall( appName )
19 AdminConfig.save()
20 def redeployApplication(pathToFile, cell, cluster, appName):
21 print "installApplicationOnServer: fileName=%s appName=%s Cell=%s Cluster=%s" % ( pathToFile, appName, cell, cluster )
22 AdminApp.install(pathToFile,'[-nopreCompileJSPs -distributeApp -nouseMetaDataFromBinary -nodeployejb -appname "'+appName+'" -createMBeansForResources -noreloadEnabled -nodeployws -MapModulesToServers [["WEB_APP_NAME" WEB_APP_NAME.war,WEB-INF/web.xml WebSphere:cell='+cell+',cluster='+cluster+' ]] -MapWebModToVH [["WEB_APP_NAME" WEB_APP_NAME.war,WEB-INF/web.xml shared_host ]] -verbose]')
23 AdminConfig.save()
24 """modify classloader model for application"""
25 deploymentID = AdminConfig.getid('/Deployment:'+appName+'/')
26 deploymentObject = AdminConfig.showAttribute(deploymentID, 'deployedObject')
27 classldr = AdminConfig.showAttribute(deploymentObject, 'classloader')
28 AdminConfig.modify(classldr, [['mode', 'PARENT_LAST']])
29 """Modify WAR class loader model"""
30 AdminConfig.show(deploymentObject, 'warClassLoaderPolicy')
31 AdminConfig.modify(deploymentObject, [['warClassLoaderPolicy', 'SINGLE']])
32 AdminConfig.save()
33 """-----------------------------------------------------------
34 Phyton script to interface with WAS Admin/Management Tools
35 -----------------------------------------------------------"""
36 if len(sys.argv) < 1:
37 print "wasAdminIntegration.py : need parameters : functionName <ARGS>"
38 sys.exit(0)
39 if(sys.argv[0] == 'clusterStop'):
40 clusterStop(sys.argv[1], sys.argv[2])
41 if(sys.argv[0] == 'clusterStart'):
42 clusterStart(sys.argv[1], sys.argv[2])
43 if(sys.argv[0] == 'clusterState'):
44 clusterState(sys.argv[1], sys.argv[2])
45 if(sys.argv[0] == 'undeployApplication'):
46 undeployApplication(sys.argv[1])
47 if(sys.argv[0] == 'appState'):
48 appState(sys.argv[1])
49 if(sys.argv[0] == 'redeployApplication'):
50 redeployApplication(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
In this code, replace "WEB_APP_NAME" with real application name or change the script so it can be passed with the rest of parameters.
For waiting part of the scripts, we will user same jython script in conjuction with bash.
Source for waitForState.sh:
view sourceprint?
01 #!/bin/bash
02 binDir=`dirname "$0"`
03 REQURED_STATE=$1
04 WAS_HOST=$2
05 WAS_PORT=$3
06 WAS_CELL=$4
07 WAS_CLUSTER=$5
08 if [ ! -n "$REQURED_STATE" ]
[ ! -n "$WAS_HOST" ]
[ ! -n "$WAS_PORT" ]
[ ! -n "$WAS_CELL" ]
[ ! -n "$WAS_CLUSTER" ];
09 then
10 echo "Usage: waitForState.sh <STATE> <HOST> <PORT> <CELL> <CLUSTER>"
11 exit 1
12 fi
13 CURR_STATE=""
14 echo "CURR_STATE: $CURR_STATE"
15 while [[ "$CURR_STATE" != "$REQURED_STATE" ]]
16 do
17 sleep 20
18 CURR_STATE="$($binDir/wsadmin.sh -conntype SOAP -lang jython -host $WAS_HOST -port $WAS_PORT -f ./wsAdminIntegration.py clusterState $WAS_CELL $WAS_CLUSTER
grep State:)"
19 echo "Current State: $CURR_STATE"
20 done
So, now we can use all these code in conjunction just by calling
view sourceprint?
1 ant fullRedeploy
Output log should be monitored for Websphere errors. If it return "Result: 99" for every operation, then everything is fine, otherwise, something went wrong.
Nice post - is there some way we can chat via - e-mail?
ReplyDelete