Accueil > Non classé > Grails – épisode 5 – Configurer et déployer

Grails – épisode 5 – Configurer et déployer

Résumé de l’épisode précédent

Nous avons vu comment installer et utiliser des plugins : la sécurité et les flux RSS ont été intégrés. Comment faire maintenant pour exploiter cette application ? Comment la déployer sur un Jboss ou un Tomcat ? Quelles sont les possibilités de configuration ?

Relire l’épisode 4.

Grails et l’environnement

Non il ne s’agit pas de parler écologie ici.

Dans chaque projet Grails, une notion d’environnement est présente. Pour chaque environnement il est possible de configurer l’application de façon spécifique:

  • dev, c’est l’environnement par défaut
  • test, utilisé lors du lancement des tests
  • prod, l’environnement configuré pour la production

Regardez le fichier grails-app/conf/Config.groovy. Une section environnements est décrite:


1
2
3
4
5
6
7
8
9
10
11
environments {
    production {
        grails.serverURL = "http://www.changeme.com"
    }
    development {
        grails.serverURL = "http://localhost:8080/${appName}"
    }
    test {
        grails.serverURL = "http://localhost:8080/${appName}"
    }
}

Cette configuration permet de régler l’URL du serveur qui héberge l’application. Cette URL sera utilisée par exemple lors de la génération de lien absolu (comme ceux que nous avons généré dans notre flux RSS, cf: épisode 4). Ce genre de bloc peut être écrit dans n’importe quel fichier de configuration Groovy. Regardez dans le fichier grails-app/conf/DataSource.groovy : un autre bloc permet de définir les propriétés de la datasource par environnement.

Profitons-en pour affiner cette configuration…

Partons du principe que :

  • la base de développement est locale (sur votre machine) et à chaque déploiement, elle est mise à jour mais pas effacée.
  • la base de test est une base hébergée sur un serveur quelconque. Elle est effacée puis recréée à chaque démarrage de l’application.
  • la base de production est une autre base hébergée sur un vrai serveur. Le schéma doit être validé au démarrage.

Pour chacun de ses environnements, les paramètres de connexion ne seront pas les mêmes :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
environments {
    development {
        dataSource {
            username = "cyril"
            password = "topsecret_local"
            dbCreate = "update"
            url = "jdbc:postgresql://127.0.0.1:5432/albums"
        }
    }
    test {
        dataSource {
            username = "albums_test"
            password = "topsecret_test"
            dbCreate = "create-drop"
            url = "jdbc:postgresql://192.168.0.254:5432/albums"
        }
    }
    production {
        dataSource {
            username = "zCGloz8912ghs5"
            password = "*jUhGGQ7120°122"
            dbCreate = "validate"
            url = "jdbc:postgresql://proddbserver/albums"
        }
    }
}

Voilà plusieurs configurations de DataSource, mais comment construire notre application en fonction d’un environnement ?

Exploiter l’application

Nous avons vu que pour lancer notre application, il suffit de taper cette commande :

1
grails run-app

En faisant cela, Grails compile notre code et lance un conteneur dédié dans lequel l’application va se déployer automatiquement. Pour créer un WAR exploitable, cette commande vous sera utile :

1
grails ENV war

ou “ENV” peut prendre comme valeur : dev, test ou prod. Construire un WAR avec les paramètres de production revient à faire :

1
grails prod war #équivalent à grails war

Une fois notre WAR construit, vous pouvez le déployer dans divers serveurs d’application / conteneurs de servlets. La documentation officielle vous aidera à déployer sur votre serveur favori : http://www.grails.org/Deployment

Configuration avancée

Configuration des logs

Log4j est directement intégré à Grails. Regardez dans le fichier grails-app/conf/Config.groovy :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// log4j configuration
log4j = {
    // Example of changing the log pattern for the default console
    // appender:
    //
    //appenders {
    //    console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
    //}

    error  'org.codehaus.groovy.grails.web.servlet',  //  controllers
           'org.codehaus.groovy.grails.web.pages', //  GSP
           'org.codehaus.groovy.grails.web.sitemesh', //  layouts
           'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
           'org.codehaus.groovy.grails.web.mapping', // URL mapping
           'org.codehaus.groovy.grails.commons', // core / classloading
           'org.codehaus.groovy.grails.plugins', // plugins
           'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
           'org.springframework',
           'org.hibernate'

    warn   'org.mortbay.log'
}

Ajoutons le niveau info sur les classes de notre application:

1
info 'com.excilys.grails'

Notez que ce bloc peut être déplacé dans le bloc environnement: la configuration du logging peut en effet être fortement différente selon que l’application soit déployée en test ou en production par exemple. Il devient également possible de complètement supprimer cette configuration pour l’environnement prod uniquement: cela permettra de la déléguer à JBoss par exemple (via un fichier jboss-log4j.xml, ou autre).

Pour avoir plus d’informations à ce sujet : http://www.grails.org/Logging

Inclusion de fichiers de propriétés

Il n’est pas rare dans un projet de vouloir sortir sa configuration du WAR applicatif. Le plus généralement, on sort ce genre de configuration dans des fichiers “properties” (c’est du moins une des méthodes les plus simples).

Grails prévoit aussi d’importer ce genre de fichier. Il permet même de les écrire sous la forme de script Groovy !

Commençons par créer un fichier nommé album_special_configuration.groovy:

1
touch grails-app/conf/album_special_configuration.groovy

L’inclusion se fait directement dans le fichier grails-app/conf/Config.groovy:


1
grails.config.locations = [ "classpath:album_special_configuration.groovy" ]

Il suffit maintenant de rajouter des propriétés de configuration que l’on pourra récupérer plus tard dans l’application:


1
album.config.ourfirstconfig = "une configuration de test"

Pour tester cette configuration, nous allons créer un test d’intégration sur le contrôleur Song.

1
grails create-integration-test com.excilys.grails.Song

Editez ce test, grails-app/test/integration/com/excilys/grails/SongTests.groovy :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.excilys.grails
import grails.test.*
class SongTests extends GrailsUnitTestCase {
 // ce bean est auto-injecté par Grails
 def grailsApplication

 protected void setUp() {
     super.setUp()
 }

 protected void tearDown() {
     super.tearDown()
 }

 void testProperty() {
 assertNotNull(grailsApplication)

 // grâce à Groovy, grailsApplication.config = this.grailsApplication.getConfig()
 def ourfirstProperty = grailsApplication.config.album.config.ourfirstconfig

 log.info ( ourfirstProperty ?: "Non trouvé :(")

 assertEquals (grailsApplication.config  .album.config.ourfirstconfig,
     "une configuration de test")
 }
}

Pour rappel, un bean présent dans le contexte Spring (qu’il soit déclaré par Grails ou ajouté par vos soins, cf: episode 2) est automatiquement injecté. Par défaut, l’injection est dite “byName”: un bean nommé “grailsApplication” est donc présent dans le contexte (en l’occurence c’est un des beans central initialisé par Grails lui-même).

Grâce à ce bean, nous allons pouvoir accéder à la configuration complète de notre application.

Lancez le test:

1
grails test-app -integration #pour ne lancer que les tests d'intégrations

Si vous utilisez la version 1.1.1 de Grails, les tests vont malheureusement échouer :(. La faute à un bug dans les scripts de compilation de Grails: celui-ci ne copie pas votre nouveau fichier de configuration dans l’application. Pour remédier à cela, il “suffit” de copier manuellement ce fichier au bon endroit:

1
cp -v grails-app/conf/album_special_configuration.groovy ~/.grails/1.1.1/projects/albums/classes/

Relancez le test. Cette fois-ci, tout est ok. Vous pouvez consulter le rapport d’execution ici:

1
vi test/reports/TEST-com.excilys.grails.SongTests.xml

Ce rapport confirme bien que la valeur de la propriété “album.config.ourfirstconfig” a été correctement récupérée.

Maintenant, récupérons ces valeurs ailleurs que dans un simple test. Éditez le contrôleur Song et ajoutez un référence vers l’objet le bean “grailsApplication”:


1
2
3
4
5
6
7
package com.excilys.grails
import org.codehaus.groovy.grails.plugins.springsecurity.Secured
class SongController {

    def grailsApplication
    //// [...]
}

Maintenant, créons une nouvelle action nommé “configuration” dans laquelle nous allons simplement faire afficher à l’écran la valeur de la propriété (en somme on fait la même chose que dans le test):


1
2
3
4
5
    def configuration = {
        def myProperty = """La valeur de la configuration est égale à
            '${grailsApplication.config.album.config.ourfirstconfig}' :)"""

        render myProperty
    }

Ce qui nous donne http://localhost:8080/albums/song/configuration :

La valeur de la configuration affichée par un contrôleur

La valeur de la configuration affichée par un contrôleur

Si vous souhaitez avoir plus d’infos sur les possibilités de configuration, consultez cette page : http://www.grails.org/Configuration

Url Mapping

Lorsqu’un utilisateur demande l’url http://localhost:8080/albums/album/create, Grails exécute le code présent dans l’action “create” du contrôleur “AlbumController”.

Ce comportement est modifiable et est défini dans le fichier grails-app/conf/UrlMappings.groovy


1
2
3
4
5
6
7
8
9
10
11
class UrlMappings {
    static mappings = {
      "/$controller/$action?/$id?"{
          constraints {
             // apply constraints here
          }
      }
      "/"(view:"/index")
      "500"(view:'/error')
    }
}

Par défaut, l’url http://localhost:8080/albums/album/create correspond parfaitement au premier mapping (le “?” fait office de test, est ce que la variable qui précéde le “?” est non-null et non vide ?) :


1
2
3
4
 "/$controller/$action?/$id?"{
    constraints {
    }
}

Il est possible de rajouter de nouveaux mappings. Exemple:

Nous souhaitons afficher les chansons d’un artist donné (chaque chanson possède un attribut nommé artistName). En l’état actuel des choses, il est possible de créer une action nommé “byartist” dans le contrôleur Song, cette action sera accessible via l’url http://localhost:8080/albums/song/byartist . Cependant, nous souhaitons que le nom de l’artiste soit affiché dans l’url mais pas l’action: http://localhost:8080/albums/all/ARTIST_NAME. Si nous ne changeons pas le mapping par défaut, alors nous sommes obligé de définir un contrôleur nommé ‘All’ et une action pour chaque artiste dans ce même contrôleur ! Ce qui, vous le conviendrez, n’est pas envisageable.

Modifiez votre mapping et ajoutez:


1
2
3
4
"/all/$artistname"{
  controller = "song"
  action = "byartist"
}

Ajoutez l’action “byartist” dans le contrôleur Song (cette action est publique, on ne la protège pas):


1
2
3
4
5
def byartist = {
      // le "params.artistname" est directement déduit grâce au mapping
      def songs = Song.findAllByArtistName (params.artistname, params)
      render (view:'list', model:[ songInstanceList: songs , songInstanceTotal : songs.size()])
}

Cette action charge la liste des chansons en s’aidant d’un “dynamic finder”, puis réutilise la vue “song/list.gsp” pour l’affichage.

Lancez l’application:

1
grails run-app

Créez quelques chansons puis essayez : http://localhost:8080/albums/all/VOTRE_ARTISTE

Consultez la documentation pour plus d’informations à propos du mapping : http://grails.org/URL+mapping

Gérer les dépendances

Dans un cadre professionnel, il est impensable (et impossible) de gérer les dépendances d’un projet de façon manuelle. Grails n’est malheureusement pas livré avec un gestionnaire de dépendances (même si un fichier de configuration Ivy est généré à la création du projet, il est quand même nécessaire d’installer Ivy mais nous y reviendrons). Néanmoins, Grails sait rester souple :D

Intégration de Maven

Installez une version supérieure ou égale à la version 2.0.9 de Maven sur votre machine. Le reste de l’integration est l’affaire d’un plugin et d’un archetype.

Partons du principe que nous souhaitons “mavéniser” notre projet Albums. Commencez par éditer le fichier ~/.m2/settings.xml (ce fichier est utilisé par Maven). S’il n’existe pas créez le :


1
2
3
4
5
<settings>
 <pluginGroups>
 <pluginGroup>org.grails</pluginGroup>
 </pluginGroups>
</settings>

Cette configuration permet d’indiquer à Maven où trouver le plugin utilisé par Grail.

Utilisez l’archétype pour créer votre fichier pom.xml (attention, ceci va créer un nouveau projet ! créez le “à coté” puis récupérez simplement le pom.xml) :


1
2
3
4
5
mvn org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-4:generate \
 -DarchetypeGroupId=org.grails \
 -DarchetypeArtifactId=grails-maven-archetype \
 -DarchetypeVersion=1.1 \
 -DgroupId=com.excilys.grails -DartifactId=albums

Voilà notre fichier pom.xml. Puisque nous avons déjà notre projet fonctionnel, nous allons compiler le tout avec Maven :

1
mvn clean install

Vous devriez avoir une erreur :(. En effet, il a un (petit) bug dans l’archetype: celui-ci oublie d’inclure une dépendance essentielle vers Ehcache. Editez donc ce fichier pom.xml et rajoutez cette dépendance. On en profite au passage pour rajouter la dépendance à Postgresql (qui rappelez-vous avait été copiée manuellement dans l’épisode 2):


1
2
3
4
5
6
7
8
9
10
11
12
13
<dependency>
 <groupId>net.sf.ehcache</groupId>
 <artifactId>ehcache</artifactId>
 <version>1.6.2</version>
 <scope>runtime</scope>
 </dependency>

 <dependency>
 <groupId>postgresql</groupId>
 <artifactId>postgresql</artifactId>
 <version>8.3-603.jdbc4</version>
 <scope>runtime</scope>
 </dependency>

Relancez la construction:

1
mvn clean install

Si vous avez des soucis avec la version de l’application, vérifiez que celle affichée dans le fichier application.properties est la même que dans le pom.

Si vous avez encore des problèmes avec votre compilation, n’hésitez pas à télécharger le code source de l’épisode et à compiler celui-ci.

L’avantage de Maven, c’est qu’il n’est pas seulement qu’un gestionnaire de dépendances: vous pouvez faire des releases, gérer votre équipe et bien d’autres choses …

Si vous n’avez besoin que d’un simple gestionnaire de dépendances, utilisez Ivy.

Intégration d’Ivy

On peut presque dire qu’Ivy est à demi-intégré dans Grails : le fichier de configuration (ivy.xml) est généré à chaque projet. Il est cependant nécessaire d’épurer ce fichier car il contient des dépendances vers des versions qui ne sont plus utilisées et donc potentiellement conflictuelles.

Editez votre fichier ivy.xml et enlevez toutes les dépendances sauf celle pour Postgresql :


1
2
3
4
5
6
7
8
9
10
11
12
<ivy-module version="2.0">
 <info organisation="org.example" module="albums"/>
 <configurations defaultconfmapping="build->default;compile->compile(*),master(*);test,runtime->runtime(*),master(*)">
 <conf name="build"/>
 <conf name="compile"/>
 <conf name="test" extends="compile"/>
 <conf name="runtime" extends="compile"/>
 </configurations>
 <dependencies>
 <dependency org="postgresql" name="postgresql" rev="8.3-603.jdbc3" conf="runtime"/>
 </dependencies>
</ivy-module>

Ivy est ensuite intégré à Grails via un plugin. Installez ce plugin:

1
grails install-plugin ivy

Ce plugin ajoute une nouvelle commande à l’écosystème de Grails : “get-dependencies”. Vous l’avez deviné, c’est à l’appel de cette commande que le gestionnaire de version ira lire le fichier de configuration et rapatriera les librairies nécéssaires :

1
grails get-dependencies

Conclusion

Vous voilà armé pour le monde de Grails. Evidemment, il reste encore des tas de notions à voir, cette petite série de billets n’étant en soi qu’une simple introduction.

Car même si Grails est une technologie prometteuse, il n’en reste pas moins qu’elle souffre de sa jeunesse. A mon avis, l’intégration de Maven est encore un gros point noir puisque très incomplète et franchement approximative. Néanmoins, Grails apporte son lot majeur d’idées intéressantes permettant une élaboration très rapide et robuste d’application. Cette rapidité d’écriture est ainsi parfaite pour l’élaboration de prototypes ou de petites et moyennes applications.

Formation Grails en 1 jour

Ressources

Code source de l’épisode  : albums_episode5.zip
Tag SVN sur Google Code : http://excilys.googlecode.com/svn/projects/grails-albums/tags/grails-albums_article_5/
Documentation sur la notion d’environnement Grails : http://www.grails.org/Environments
Documentation sur le logging : http://www.grails.org/Logging
Documentation sur la configuration : http://www.grails.org/Configuration
Documentation sur l’URL mapping : http://grails.org/URL+mapping
Documentation Maven : http://www.grails.org/Maven+Integration

<scope>runtime</scope>
Share
  1. Pierre-Yves RICAU
    19/01/2010 à 16:36 | #1

    Ces notions de configuration et déploiement sont très importantes pour dépasser le stade du jouet et utiliser Grails à des fins plus sérieuses, merci pour ce billet !

    Deux petites questions :

    Pourquoi avoir utilisé le plugin Maven Archetype en version 2.0-alpha-4 ? Est-ce parce qu’il n’y a pas d’archetype pour Grails dans les versions antérieures ? Ce plugin est cool, mais bizarre quand même… ils ne pourraient pas définir des dépôts où aller chercher la liste des archetypes, plutôt que d’avoir une liste fixe pour chaque release ? A moins que je ne me trompe. Mais je m’éloigne du sujet…

    A partir du moment où tu créé le mapping pour l’url “http://localhost:8080/albums/all/ARTIST_NAME”, est-ce que l’url “http://localhost:8080/albums/song/byartist” est toujours disponible ? Je suppose que oui. Disposes-t’on d’un moyen de la désactiver ?

  2. Cyril BROUILLARD
    19/01/2010 à 17:21 | #2

    @Pierre-Yves RICAU
    “Pourquoi avoir utilisé le plugin Maven Archetype en version 2.0-alpha-4 ?”
    A l’époque ou j’ai écrit l’article, c’était la dernière version disponible. Tu peux regarder le tableau de correspondance versionPlugin/versionGrails ici : http://www.grails.org/Maven+Integration?xhr=true

    “Ce plugin est cool, mais bizarre quand même” … alors là ! je suis on ne peut plus d’accord avec toi ! J’ai pas trouvé l’intégration Maven bien aboutie (même si ça a quand même bien progressé). Il reste encore du taf sur cette partie là.

    “partir du moment où tu créé le mapping pour l’url « http://localhost:8080/albums/all/ARTIST_NAME », est-ce que l’url « http://localhost:8080/albums/song/byartist » est toujours disponible ?” Oui bien sûr. Tout se passe dans le fameux fichier UrlMappings. Le désactiver revient à supprimer le bloc de configuration adéquat. Si je ne m’abuse, Grails prend et applique les blocs en fonction de leur degré de précision : du plus précis au plus vague … http://grails.org/URL+mapping

    Salut ;)

  3. Romain MALLARD
    07/05/2010 à 18:12 | #3

    Merci beaucoup pour la série des billets sur Grails, c’était très intéressant :-)

  1. 12/03/2015 à 01:17 | #1
  2. 21/12/2015 à 23:34 | #2