2

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

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

4

3 回答 3

3

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

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

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

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

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

<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(“简介”和“上下文参数”部分)。

于 2010-11-09T16:20:47.987 回答
2

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

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

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

于 2009-06-17T09:34:11.337 回答
1

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

  1. 为 ServletContext 编写了一个动态代理,它拦截返回适当值的 getInitParameter 和 getInitParameterNames 方法(在这种情况下来自特定于环境的属性文件)
  2. 编写了一个非常小的 FacesContextFactoryImpl 子类,它将 first/servletcontext 参数代理到 getFacesContext,然后委托给超类。
  3. 将 faces-context-factory 子句添加到我的 faces-config 命名我的 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() 看起来像是另一天的练习,但它只返回应用程序应该使用的属性值映射

于 2010-01-25T08:18:43.210 回答