是否可以覆盖不同环境的facelets.development?

时间:2009-06-16 21:50:24

标签: jsf java-ee facelets

我们想将facelets.development设置为false以抑制非开发环境中的堆栈跟踪,但是我们希望在dev中将其设置为true以进行调试。

我们的部署过程规定了一个通过环境迁移到生产环境的CI构建,因此我们无法使用需要为每个环境重建app / rewriting web.xml的方法。我们想根据属性文件设置更改应用程序的值。这可能吗?应用程序如何访问facelets.development?

3 个答案:

答案 0 :(得分:3)

我认为最简单的方法是将Context参数放在web.xml中:

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

并在开发部署中覆盖它。这通常可以在不改变WAR的情况下实现。在Tomcat中,使用此行(在<Context> ... </Context>内)包括WAR中的META-INF / context.xml:

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

Tomcat会在启动时将此文件复制到$ CATALINA_BASE / conf / [enginename] / [hostname] / [context-path-name] .xml,可用于配置以外的webapp 战争。这将在您的每个环境中发生,管理员只需将其更改为:

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

之后,即使部署了具有较新/META-INF/context.xml的新WAR,Tomcat也不会覆盖它。 context参数的名称必须与WEB-INF / web.xml中的声明相匹配。

有关详细信息,请参阅http://tomcat.apache.org/tomcat-6.0-doc/config/context.html(“简介”和“上下文参数”部分)。

答案 1 :(得分:2)

我可以想到几种方法来做到这一点,其中没有一个非常令人愉快。

  • 装饰FacesContext以编程方式控制init parameters。这是一项很少有收获的工作。
  • 修补FaceletViewHandler课程以获得您想要的行为。如果升级Facelets库,这可能会增加维护开销。可能会使那些在生产中管理应用程序的人感到不快。
  • 补丁方法的一个变体就是在dev / test机器中使用已修补的JAR并将它们放入服务器库中 - 然后使用PARENT_FIRST类加载将它们加载到应用程序中的JAR(假设您的应用服务器支持所有这些)。这样做的缺点是它强加了类加载策略,你必须在整个地方管理JAR。

我赞成其他一些方法。如果测试计算机上需要此设置,则部署脚本可能会在安装期间修改这些服务器的应用程序。如果要在源代码管理中将其设置为true,则构建脚本可以在构建过程中将其删除。这种方法对运行时代码没有影响。

答案 2 :(得分:1)

我已经对上面的选项1实施了变体。例如

  1. 为ServletContext写了一个动态代理,拦截getInitParameter和getInitParameterNames方法,返回适当的值(在这种情况下来自环境特定的属性文件)
  2. 编写了一个非常小的FacesContextFactoryImpl子类,它将第一个/ servletcontext参数代理到getFacesContext,然后委托给超类。
  3. 在faces-config中添加了faces-context-factory子句,命名我的FacesContextFactoryImpl类
  4. 我已经有了一个机制来加载特定于环境的属性文件,并使它们可以作为应用程序的Properties对象使用(第2点中的工厂将这些属性传递给第1点中的代理,以用作替代源initParameter值)
  5. 代理看起来像:

    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;
        }
    }
    

    工厂看起来像:

    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);
        }
    }
    

    并且faces-config看起来像

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

    SystemContext.getInstance()。getConfigProperties()看起来像是另一天的练习,但它只返回app应该使用的属性值Map