mardi 24 mai 2016

Externaliser la configuration d'une webapp


Comment permettre à une application web Java de récupérer un paramètre de configuration sans avoir à repackager le ".war" ?



Si on s'interdit de modifier le fichier ".war" ça veut dire que l'on ne peut plus utiliser les possibilités de configuration à partir des fichiers qu'il contient ( web.xml, fichiers properties, etc ).
Les paramètres de configuration devront donc être portés par les environnements d'exécution de l'application (et non par l'application elle même), c'est à dire le server d'applications ( Tomcat, Jetty, etc ) ou le système d'exploitation.
Les trois possibilités les plus courantes sont décrites ci-après...

1) Utiliser une variable d'environnement

- Définir la variable d'environnement au niveau du système d'exploitation
Exemple sous Linux  :
   export MYFOLDER=/tmp/foo

- Récupérer la valeur de cette variable dans le code Java,
  par exemple dans une classe de type  "ServletContextListener" :
    String value = System.getenv("MYFOLDER")

Cette solution est simple et applicable indépendamment type de serveur d'applications.
Mais elle nécessite une intervention au niveau "système d'exploitation" (pas toujours possible) et on ne peut avoir qu'une seule valeur pour toutes les instances de serveurs d'application sur un même OS.


2) Configurer une propriété de type "Java System property"

Il s'agit de définir une propriété pour la JVM au lancement du serveur d'applications.

- Définir la propriété pour un serveur d'applications particulier :
Soit avec l'option "-D" de la ligne de commande
Soit en utilisant les fichiers de configuration propres à chaque serveur d'application
Exemple pour Tomcat :
        Dans le fichier "catalina.properties" de Tomcat :
    myfolder=/tmp/foo
       
        NB : pour un lancement dans Eclipse utiliser le fichier "catalina.properties"
                 situé dans le workspace (dans  "Servers" )

- Récupérer la valeur de cette propriété dans le code Java :
    String value = System.getProperty("myfolder");


3) Configurer une "valeur nommée" accessible via JNDI

Une "ressource nommée" peut être définie au niveau du serveur d'applications, elle sera ensuite récupérée via JNDI.

- Définir la "ressource nommée" pour le serveur d'applications concerné

Pour Tomcat :
        Dans le fichier "web.xml" de Tomcat (fichier global pour toutes les webapps) :
      <env-entry>
          <env-entry-name>myTextValue</env-entry-name>
          <env-entry-type>java.lang.String</env-entry-type>
          <env-entry-value>foo</env-entry-value>
      </env-entry>
      <env-entry>
          <env-entry-name>myMaxValue</env-entry-name>
          <env-entry-type>java.lang.Integer</env-entry-type>
          <env-entry-value>10</env-entry-value>
      </env-entry>
        NB :
        - respecter l'ordre des tags "name/type/value"
        - pour un lancement dans Eclipse utiliser le fichier "web.xml" situé
          dans le workspace (dans  "Servers" )
       
        cf https://tomcat.apache.org/tomcat-7.0-doc/config/globalresources.html#Environment_Entries 

Pour Jetty :
        Dans le fichier "jetty.xml" ajouter une ressource avec une entrée du type :
     <New class="org.eclipse.jetty.plus.jndi.EnvEntry">
       <Arg></Arg> <!-- scope : empty = JVM scope-->
       <Arg>myTextValue</Arg> <!-- name -->
       <Arg type="java.lang.String">foo</Arg> <!-- value-->
       <Arg type="boolean">true</Arg>  
     </New>

        cf http://www.eclipse.org/jetty/documentation/current/using-jetty-jndi.html

- Récupérer la valeur de cette variable dans le code Java
  Exemple pour une chaine de caractères :

   String name = "myTextValue" ;
   try {
      Object value = InitialContext.doLookup("java:comp/env/"+name);
      System.out.println("JNDI OBJECT '"+ name + "' = '" + value + "'");
   } catch (NamingException e) {
      System.out.println("JNDI OBJECT ERROR : " + e.getMessage());
      e.printStackTrace();
   }