2009-06-16 5 views
2

Nous aimerions définir facelets.development sur false pour supprimer les traces de pile dans des environnements non développés, mais nous aimerions qu'il soit défini sur true dans dev pour le débogage. Notre processus de déploiement impose une génération de CI qui est migrée à travers les environnements jusqu'à la production, donc nous ne pouvons pas utiliser une approche qui nécessite la reconstruction de l'application/réécriture web.xml pour chaque environnement. Nous aimerions changer la valeur de l'application, en fonction d'un paramètre de fichier de propriétés. Est-ce possible? Comment l'application peut-elle accéder à facelets.development?Est-il possible de surcharger le développement des facelettes pour différents environnements?

Répondre

2

je peux penser à quelques façons de faire, aucun d'entre eux très agréable.

  • ornez les FacesContext pour contrôler le init parameters programme. C'est beaucoup de travail pour si peu de gain.
  • Corrigez la classe FaceletViewHandler pour obtenir le comportement souhaité. Cela peut ajouter une surcharge de maintenance si vous mettez à niveau vos bibliothèques Facelets. Peut rendre malheureux les gens qui gèrent l'application en production.
  • Une variante de l'approche de patch est d'utiliser simplement les fichiers JAR patchés dans vos machines dev/test et les mettre dans le serveur libs - puis utilisez PARENT_FIRST classloading pour les charger sur JARs dans les applications (en supposant que votre serveur d'applications prend en charge tout ce qui). L'inconvénient de ceci est qu'il impose des politiques de chargement en classe et que vous devez gérer les JARs partout.

je préfère opter pour une autre approche. Si ce paramètre est requis sur les machines de test, un script de déploiement peut éventuellement modifier l'application pendant l'installation de ces serveurs. Si vous voulez qu'il soit défini sur true dans votre contrôle de source, le script de construction pourrait le supprimer dans le cadre du processus de construction. Cette approche n'aurait aucun impact sur votre code d'exécution.

1

J'ai implémenté une variante de l'option 1 ci-dessus. par exemple

  1. a écrit un proxy dynamique pour un ServletContext qui intercepte les getInitParameter et les méthodes de getInitParameterNames retour des valeurs appropriées (provenant de fichiers de propriétés spécifiques à l'environnement dans ce cas)
  2. a écrit une sous-classe très faible de FacesContextFactoryImpl qui proxie la première/servletcontext paramètre à getFacesContext puis délègue à la superclasse.
  3. a ajouté une clause faces contexte usine à mes nommer visages-config ma classe FacesContextFactoryImpl
  4. j'avais déjà un mécanisme en place pour charger les fichiers de propriétés spécifiques à l'environnement et les mettre à disposition sous forme d'objet Propriétés à l'application (la usine au point 2 transmet ces propriétés à la procuration au point 1 à utiliser comme une source alternative de valeurs InitParamètre)

la procuration ressemble à:

package zzzzz.framework.context; 

import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import java.lang.reflect.Proxy; 
import java.util.Enumeration; 
import java.util.Hashtable; 
import java.util.Map; 

import javax.servlet.ServletContext; 

import org.apache.commons.collections.IteratorUtils; 
import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 

/** 
* A proxy for ServletContext that intercepts accesses to the initParameters and 
* returns values from the specified params instead. Generally useful if we have 
* a set of properties (eg SystemContext.getInstance().getConfigProperties()) that 
* we want to use in preference to the webapp initProperties. 
* 
* 
*/ 
public class ServletContextProxy 
     implements InvocationHandler { 
    @SuppressWarnings("unused") 
    private static final Log log = LogFactory.getLog(ServletContextProxy.class); 

    @SuppressWarnings("unchecked") 
    public static ServletContext newInstance(ServletContext subject, 
      Map params) { 
     return newInstance(subject, 
       params, 
       true); 
    } 

    @SuppressWarnings("unchecked") 
    public static ServletContext newInstance(ServletContext subject, 
      Map params, 
      boolean overrideInitValues) { 
     return (ServletContext) Proxy.newProxyInstance(subject.getClass() 
       .getClassLoader(), 
       subject.getClass() 
         .getInterfaces(), 
       new ServletContextProxy(subject, 
         params, 
         overrideInitValues)); 
    } 

    /** 
    * A convenience method to help extracting the initParameters from a 
    * ServletContext because it doesn't expose it's underlying Map 
    * 
    * @param config 
    * @return 
    */ 
    @SuppressWarnings("unchecked") 
    protected static Map copyInitParameters(Map parms, 
      ServletContext config) { 
     Enumeration names = config.getInitParameterNames(); 
     // copy all the existing initParameters 
     while (names.hasMoreElements()) { 
      String name = (String) names.nextElement(); 
      parms.put(name, 
        config.getInitParameter(name)); 
     } 
     return parms; 
    } 

    private boolean overrideInitValues = true; 

    @SuppressWarnings("unchecked") 
    private Map params; 
    private ServletContext subject; 

    @SuppressWarnings("unchecked") 
    public ServletContextProxy(ServletContext subject, 
      Map params, 
      boolean overrideInitValues) { 
     this.subject = subject; 
     this.overrideInitValues = overrideInitValues; 
     this.params = new Hashtable(); 
     if (this.overrideInitValues) { // default behaviour... supplied parameters win 
      // start with initParameters 
      copyInitParameters(this.params, 
        subject); 
      // override and supplement with supplied params 
      if (params != null) { 
       this.params.putAll(params); 
      } 
     } else { 
      // start with supplied params 
      if (params != null) { 
       this.params.putAll(params); 
      } 
      // override and supplement with initParameters 
      copyInitParameters(this.params, 
        subject); 

     } 
    } 

    public Object invoke(Object proxy, 
      Method m, 
      Object[] args) throws Throwable { 
     Object result; 
     try { 
      if ("getInitParameter".equals(m.getName())) { 
       result = this.params.get(args[0]); 
      } else if ("getInitParameterNames".equals(m.getName())) { 
       result = IteratorUtils.asEnumeration(this.params.keySet() 
         .iterator()); 
      } else {// else let it go through to the keeper 
       result = m.invoke(this.subject, 
         args); 
      } 
     } catch (InvocationTargetException e) { 
      throw e.getTargetException(); 
     } catch (Exception e) { 
      throw new RuntimeException("unexpected invocation exception: " 
        + e.getMessage()); 
     } 
     return result; 
    } 
} 

l'usine ressemble à:

package zzz.faces.context; 

import javax.faces.FacesException; 
import javax.faces.context.FacesContext; 
import javax.faces.lifecycle.Lifecycle; 
import javax.servlet.ServletContext; 

import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 

import zzzzz.framework.context.ServletContextProxy; 
import zzzzz.context.SystemContext; 

/** 
* A FacesContextFactory implementation that supplements/overrided the 
* servletContext initParemeters with properties form 
* SystemContext.configProperties 
* <p> 
* The point of this is that it allows us to substitute configuration in the 
* web.xml like this (which requires rewriting web.xml to change) 
* </p> 
* 
* <pre> 
* &lt;!-- Enables special Facelets debug output during development --&gt; 
* &lt;context-param&gt; 
* &lt;param-name&gt;facelets.DEVELOPMENT&lt;/param-name&gt; 
* &lt;param-value&gt;true&lt;/param-value&gt; 
* &lt;/context-param&gt; 
* </pre> 
* 
* <p> 
* with settings in the relevent application.properties file like this (which 
* can be changed separately to the webapp) 
* </p> 
* 
* <pre> 
* # Enables special Facelets debug output during development 
* facelets.DEVELOPMENT=true 
* </pre> 
* 
* <p> 
* usage: add a clause to faces-config like this: 
* 
* <pre> 
* <factory> 
* <faces-context-factory>zzzzz.faces.context.FacesContextFactoryImpl</faces-context-factory> 
* </factory> 
* </pre> 
* <p> 
* 
*/ 
public class FacesContextFactoryImpl extends com.sun.faces.context.FacesContextFactoryImpl { 
    @SuppressWarnings("unused") 
    private static final Log log = LogFactory.getLog(FacesContextFactoryImpl.class); 

    public FacesContextFactoryImpl() { 
     super(); 
    } 

    @Override 
    public FacesContext getFacesContext(Object sc, 
      Object request, 
      Object response, 
      Lifecycle lifecycle) throws FacesException { 

     if (sc instanceof ServletContext 
       && !(sc instanceof ServletContextProxy)) { 
      // wrap the servlet context with a proxy to override/supplement initParameters 
      sc = ServletContextProxy.newInstance((ServletContext) sc, 
        SystemContext.getInstance() 
          .getConfigProperties(), 
        true); 
     } 
     return super.getFacesContext(sc, 
       request, 
       response, 
       lifecycle); 
    } 
} 

et les faces-config ressemble

<faces-config> 
    blah waffle.... 
     <factory> 
     <faces-context-factory>zzzz.faces.context.FacesContextFactoryImpl</faces-context-factory> 
     </factory> 
    </faces-config> 

Qu'est-ce que SystemContext.getInstance().getConfigProperties() ressemble est un excercise pour un autre jour, mais il retourne juste la carte des valeurs de propriété de l'application est censée utiliser

2

Je pense que l'approche la plus simple est de mettre le paramètre de contexte dans web.xml:

<context-param> 
    <param-name>facelets.DEVELOPMENT</param-name> 
    <param-value>false</param-value> 
</context-param> 

et remplacez-le dans vos déploiements de développement. C'est généralement possible sans changer le WAR. Dans Tomcat, inclure META-INF/context.xml dans votre guerre avec cette ligne (dans les <Context> ... </Context>):

<Parameter name="facelets.DEVELOPMENT" value="true" override="false" /> 

Tomcat copie ce fichier au démarrage à $ CATALINA_BASE/conf/[nom_moteur]/[nom d'hôte]/[nom du chemin d'accès au contexte] .xml qui peut être utilisé pour configurer la webapp en dehors du fichier WAR . Cela se produira dans chacun de vos environnements et les administrateurs ont à changer une seule fois à:

<Parameter name="facelets.DEVELOPMENT" value="false" override="false" /> 

Ensuite, Tomcat ne le remplacer, même si une nouvelle guerre avec une nouvelle /META-INF/context.xml est déployée . Les noms des contextes Les paramètres doivent correspondre aux déclarations dans WEB-INF/web.xml. Voir http://tomcat.apache.org/tomcat-6.0-doc/config/context.html pour plus de détails (section "Introduction" et "Paramètres de contexte").

Questions connexes