ANT & WLST Part 1 – Création de data sources JDBC sur Weblogic server

Qui n’a jamais dû recréer toutes les data sources sur son serveur Weblogic après la création d’un domaine ? Hélas nous sommes tous passés par là, et nous savons à quel point cette tache peut devenir pénible …
Cet article fait partie d’une série d’articles qui porteront tous sur l’industrialisation de la création & du déploiement de ressource sur Weblogic, et c’est par les data sources que nous allons commencer.
La méthode que je propose est celle que j’ai mis au point en agrégeant plusieurs méthodes. Elle est probablement perfectible, d’ailleurs je l’ai améliorée en écrivant cet article, n’hésitez donc pas à me laisser vos remarques !

Commençons par lister ce dont avons besoin …

des fichiers de propriétés :

  • build.properties, contient les propriétés globales
  • jdbc.properties, contient la description de nos data sources

un script SHELL qui va appeler notre script ANT

  • deployRessourceToServer.sh

un script ANT qui va appeler un script WLST

  • build.xml

un script WLST qui va créer les ressources

  • createJDBCResource.py

… avant d’entrer dans le détail :

Ces fichiers respectent l’organisation suivante :

  • ./config/jdbc.properties (répertoire où sont placés les fichiers de propriétés)
  • ./wlst/createJDBCResource.py (répertoire où sont placés les scripts WLST *.py)
  • ./deployRessourceToServer.sh
  • ./build.xml

Fichiers de propriétés

build.properties

Le premier fichier de propriétés build.properties contient des propriétés globales qui pourront servir à d’autres scripts de création de ressources.
Voici son contenu :
[sourcecode language= »plain »]
environnement = dev
<pre># environnement de developpement
dev.fmw.home = /orcl/Oracle/Middleware
dev.fmw.soa.home = /orcl/Oracle/Middleware/Oracle_SOA1
dev.wls.username = weblogic
dev.wls.password = weblogic1
dev.wls.server = t3://localhost:7001
dev.wls.instance = AdminServer
# Base de recette
recette.fmw.home = /oracle/Oracle/Middleware
recette.fmw.soa.home = /oracle/Oracle/Middleware/Oracle_SOA1
recette.wls.username = weblogic
recette.wls.password = weblorct1
recette.wls.server = t3://localhost:7001
recette.wls.instance = AdmînServer</pre>
[/sourcecode]
Ce fichier est divisé en deux parties :

  • La première se résume à une seule propriété « environnement » qui permet de spécifier l’environnement sur lequel les ressources seront créées.
  • La deuxième correspondent à des blocs de propriétés spécifiques à chaque environnement, ici « dev » et « recette ». On y retrouve les propriétés classique de connexions au serveur Weblogic mais aussi les répertoires HOME nécessaires.

jdbc.properties

le second fichier de propriétés jdbc.properties contient les propriétés nécessaires pour créer une data source :
[sourcecode language= »plain »]
# Cette liste contient les préfix des datasources à créer
database.to.create = base.dev,base.recette
# Base de développement
base.dev.weblogic.datasource.name = BaseDeveloppement1
base.dev.jdbc.driver.url = jdbc:oracle:thin:@base.dev.company.com:1521:DEV
base.dev.jdbc.driver.name = oracle.jdbc.xa.client.OracleXADataSource
base.dev.jdbc.db.user = DBDEV
base.dev.jdbc.db.password = DBDEV
base.dev.jdbc.db.test.table = SQL SELECT 1 FROM DUAL
# Base de recette
base.recette.weblogic.datasource.name = BaseRecette1
base.recette.jdbc.driver.url = jdbc:oracle:thin:@(DESCRIPTION=(LOAD_BALANCE=YES)(FAILOVER=YES)
(ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcp)(HOST=base.recette1.company.com)(PORT=1521))
(ADDRESS=(PROTOCOL=tcp)(HOST=base.recette2.company.com)(PORT=1521)))
(CONNECT_DATA=(SERVICE_NAME=SRV_RCT)))
base.recette.jdbc.driver.name = oracle.jdbc.xa.client.OracleXADataSource
base.recette.jdbc.db.user = DBRCT
base.recette.jdbc.db.password = DBRCT
base.recette.jdbc.db.test.table = SQL SELECT 1 FROM DUAL
[/sourcecode]
Comme vous pouvez le constater, nous avons construit le nom des propriétés en prenant soin de préfixer chaque nom de propriété par le nom de la base. Ainsi, nous pouvons passer une liste de base de données à créer (database.to.create).

Fichier ANT

L’exécution du fichier ANT nécessite l’ajout du jar ant-contrib-1.0b3.jar dans le répertoire lib de votre ANT_HOME. Ce package est disponible ici, il contient un ensemble de fonctions complémentaires qui vont vous faciliter la vie.
Voici donc notre fichier ANT :
[sourcecode language= »xml »]
<?xml version="1.0" encoding="iso-8859-1"?>
<project default="createJDBCRessource">
<!– Import des propriétés contenues dans le fichier build.properties –>
<property file="./config/build.properties"/>
<!– Partie de déclaration des scripts WLST à utiliser –>
<property name="jdbc.ressource.creation.script" value="wlst/createJDBCResource.py"/>
<!– Import des fonctions contenues dans la librairie ant contrit –>
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
<!–
la fonction propertycopy appartient à la librairie ant contrib.
Elle permet de créer une propriété à partir de la valeur d’une propriété dont le nom est reconstruit
Ici nous utilisons le contenu de la propriété "environnement" en préfixe.
Ainsi, quand par exemple, la propriété ${environnement} égale "dev"
la valeur de la propriété fmw.home sera celle de la propriété dev.fmw.home,
c’est à dire "/SOAD/SOASuite_1112" (voir ci dessus)
–>
<propertycopy name="fmw.home" from="${environnement}.fmw.home" silent="true"/>
<propertycopy name="fmw.soa.home" from="${environnement}.fmw.soa.home" silent="true"/>
<propertycopy name="wls.username" from="${environnement}.wls.username" silent="true"/>
<propertycopy name="wls.password" from="${environnement}.wls.password" silent="true"/>
<propertycopy name="wls.server" from="${environnement}.wls.server" silent="true"/>
<propertycopy name="wls.instance" from="${environnement}.wls.instance" silent="true"/>
<!– Définition du classpath pour l’exécution d’une de wlst –>
<path id="class.path">
<pathelement path="${fmw.home}/wlserver_10.3/server/lib/weblogic.jar"/>
</path>
<!– Définition de la tache wlst –>
<taskdef name="wlst" classname="weblogic.ant.taskdefs.management.WLSTTask">
<classpath>
<pathelement path="${fmw.home}/wlserver_10.3/server/lib/weblogic.jar"/>
</classpath>
</taskdef>
<!–
Cible à appeler pour créer des ressources JDBC.
l’URL de connexion au serveur, le username, le mot de passe, ainsi que l’instance
sur laquelle seront déployées les ressources, sont passés au script WLST
–>
<!– Partie de déclaration des taches ANT invocables –>
<target name="createJDBCRessource">
<echo message="importscript: ${jdbc.ressource.creation.script}"/>
<java classname="weblogic.WLST" fork="true">
<arg line="${jdbc.ressource.creation.script} ${wls.server} ${wls.username} ${wls.password} ${wls.instance}"/>
<classpath refid="class.path"/>
</java>
</target>
</project>
[/sourcecode]

Fichier WLST

Le fichier est largement commenté mais voici une description globale de ce qu’il fait :

  • Il récupère les paramètres passé par ANT et charge les propriétés JDBC
  • Il parcourt la liste des base à créer et appel pour chaque base la fonction createJDBCRessource

[sourcecode language= »python »]
import sys
from java.lang import System
from java.io import FileInputStream
from java.util import Properties
#=======================================================================================
# Common functions
# Cette partie regroupe les fonctions communes
#=======================================================================================
# Créer une session d edition
def createSession():
connect(user,password, serverURL)
edit()
startEdit()
# Sauvegarde les modifications et ferme la session en cour
def saveAndCloseSession():
save()
activate(block="true")
disconnect()
exit()
print "@@@ Fin de l’exécution @@@"
# Charge un fichier de proprietes et construit une Map
# Ce qui permet d’obtenir une valeur en fonction d’une clé
def loadProperties(fileName):
properties = Properties()
input = FileInputStream(fileName)
properties.load(input)
input.close()
result= {}
for entry in properties.entrySet():
result[entry.key] = entry.value
return result
#=======================================================================================
# @@ 3eme partie : createJDBCRessource, créée les data sources
#=======================================================================================
def createJDBCRessource(prefix):
try:
servermb=getMBean("/Servers/"+serverInstance)
if servermb is None:
print ‘@@@ No server MBean found’
else:
# Debut de la creation de la data source
print ‘n@@@ Creation de la datasource : ‘+prefix
try:
# toutes les propriétés du fichier jdbc.properties sont préfixées
# par le nom de la data source. Ainsi grâce au préfixe passé en paramètre
# de la fonction courante, nous pouvons reconstruire le nom de la propriété
# et obtenir la valeur nécessaire. Ici, si le prefixe est "base.dev" alors
# la propriété obtenu sera "base.dev.weblogic.datasource.name" dont la valeur
# est "BaseDeveloppement1" (voir jdbc.properties)
jdbcSR = create(properties[prefix+’.weblogic.datasource.name’],"JDBCSystemResource")
theJDBCResource = jdbcSR.getJDBCResource()
theJDBCResource.setName(properties[prefix+’.weblogic.datasource.name’])
# Configuration du pool de connexion
connectionPoolParams = theJDBCResource.getJDBCConnectionPoolParams()
connectionPoolParams.setConnectionReserveTimeoutSeconds(25)
connectionPoolParams.setMaxCapacity(100)
connectionPoolParams.setTestTableName(properties[prefix+’.jdbc.db.test.table’])
# Configuration du nom JNDI
dsParams = theJDBCResource.getJDBCDataSourceParams()
dsParams.addJNDIName("jdbc/"+properties[prefix+’.weblogic.datasource.name’])
# Configuration du driver
driverParams = theJDBCResource.getJDBCDriverParams()
driverParams.setUrl(properties[prefix+’.jdbc.driver.url’])
driverParams.setDriverName(properties[prefix+’.jdbc.driver.name’])
# Configuration des identifiants de connexion
driverParams.setPassword(properties[prefix+’.jdbc.db.password’])
driverProperties = driverParams.getProperties()
proper = driverProperties.createProperty("user")
proper.setValue(properties[prefix+’.jdbc.db.user’])
# Ajout de propriétés supplémentaires si elles existent
# dans le fichier de propriétés jdbc.properties
try:
if properties[prefix+’.jdbc.db.name’] is not None:
proper = driverProperties.createProperty("databaseName")
proper.setValue(properties[prefix+’.jdbc.db.name’])
except:
print "@@@ no db name property"
try:
if properties[prefix+’.jdbc.db.server’] is not None:
proper = driverProperties.createProperty("serverName")
proper.setValue(properties[prefix+’.jdbc.db.server’])
except:
print "@@@ no server name property"
try:
if properties[prefix+’.jdbc.db.port’] is not None:
proper = driverProperties.createProperty("portNumber")
proper.setValue(properties[prefix+’.jdbc.db.port’])
except:
print "@@@ no port number property"
# Configuration de la cible
jdbcSR.addTarget(getMBean("/Servers/"+serverInstance))
except:
print "@@@ La data source existe deja "
except:
print "@@@ Unexpected error:", sys.exc_info()[0]
raise
#=======================================================================================
# @@ 2eme Partie : Main, fonction principale du script
#=======================================================================================
def main():
try:
# Création d’une session d’exécution du script
createSession()
# Récupération de la liste des data source à créer
databaseToCreate = properties[‘database.to.create’]
print "@@@ Datasource to create " + databaseToCreate
# La liste des bases à créer est éclatée en plusieurs
# éléments grâce au séparateur spécifié.
# Pour chaque élément de la liste,
# la méthode createJDBCRessource est appelée
for databasePrefix in databaseToCreate.split(‘,’):
createJDBCRessource(databasePrefix)
# Sauvegarde et fermeture de la session
saveAndCloseSession()
except:
print "Unexpected error: ", sys.exc_info()[0]
dumpStack()
raise
#=======================================================================================
# @@ 1ere Partie : exécutée lors de l’appel du script WLST
#=======================================================================================
print "@@@ Execution de la procedure de creation des data sources @@@"
try :
# Toutes les variables initialisées dans ce bloc
# deviennent globales
# Recuperation des arguments transmises par ANT
serverURL = sys.argv[1]
user = sys.argv[2]
password = sys.argv[3]
serverInstance = sys.argv[4]
# Chargement du fichier de proprietes
properties = loadProperties("config/jdbc.properties")
# Execution
main()
except:
print "Unexpected error: ", sys.exc_info()[0]
dumpStack()
raise
[/sourcecode]

Fichier SH

[sourcecode language= »shell »]
#!/bin/bash
# Changer les variables suivantes en fonction
# de votre configuration
JAVA_HOME=/usr/java/jdk1.6.0_18
export JAVA_HOME
ANT_HOME="/orcl/Oracle/Middleware/modules/org.apache.ant_1.7.1"
export ANT_HOME
### Creation des ressource JMS communes
if [ "$1" = "JDBC" ]
then
# Creation des ressources JDBC
${ANT_HOME}/bin/ant -f build.xml createJDBCRessource
else
echo ""
echo "Argument invalide, l’argument doit etre l un des suivants : "
echo ""
echo "JDBC : creation des data sources"
echo ""
echo ""
fi
[/sourcecode]

Et comment j’utilise tout cela ?

Vous n’avez plus qu’à lancer le fichier SH et le tour est joué !
Cette article va nous servir de base technique pour tous les articles suivants, vous allez voir que le gros du travail est fait, et qu’il ne reste plus qu’à ajouter des blocs de code. A bientôt !