【问题标题】:Is it possible to override facelets.development for different environments?是否可以针对不同的环境覆盖 facelets.development?
【发布时间】:2010-11-03 11:58:42
【问题描述】:

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

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

【问题讨论】:

    标签: jsf jakarta-ee facelets


    【解决方案1】:

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

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

    并在您的开发部署中覆盖它。这通常可以在不更改 WAR 的情况下实现。在 Tomcat 中,将 META-INF/context.xml 包含在您的 WAR 中,使用这一行(在 &lt;Context&gt; ... &lt;/Context&gt; 内):

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

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

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

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

    详见http://tomcat.apache.org/tomcat-6.0-doc/config/context.html(“简介”和“上下文参数”部分)。

    【讨论】:

      【解决方案2】:

      我可以想到几种方法来做到这一点,但都不是很愉快。

      • 装饰FacesContext 以编程方式控制init parameters。这么多工作却收效甚微。
      • 修补FaceletViewHandler 类以获得您想要的行为。如果您升级 Facelets 库,这可能会增加维护开销。可能会让在生产环境中管理应用的人不开心。
      • 修补方法的一种变体是在您的开发/测试机器中使用已修补的 JAR 并将它们放入服务器库中 - 然后使用 PARENT_FIRST 类加载通过应用程序中的 JAR 加载它们(假设您的应用服务器支持所有)。这样做的缺点是它强制执行类加载策略,并且您必须在所有地方管理 JAR。

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

      【讨论】:

        【解决方案3】:

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

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

        代理看起来像:

        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() 看起来像是另一天的练习,但它只是返回应用程序应该使用的属性值映射

        【讨论】:

          猜你喜欢
          • 2018-08-12
          • 1970-01-01
          • 2019-01-07
          • 2020-01-14
          • 1970-01-01
          • 2021-12-28
          • 1970-01-01
          • 2021-05-24
          • 2017-05-28
          相关资源
          最近更新 更多