我们希望将 facelets.development 设置为 false 以在非开发环境中抑制堆栈跟踪,但我们希望在 dev 中将其设置为 true 以进行调试。
我们的部署过程要求一个 CI 构建通过环境迁移到生产,因此我们不能使用需要为每个环境重建应用程序/重写 web.xml 的方法。我们想根据属性文件设置更改应用程序的值。这可能吗?应用程序如何访问 facelets.development?
我们希望将 facelets.development 设置为 false 以在非开发环境中抑制堆栈跟踪,但我们希望在 dev 中将其设置为 true 以进行调试。
我们的部署过程要求一个 CI 构建通过环境迁移到生产,因此我们不能使用需要为每个环境重建应用程序/重写 web.xml 的方法。我们想根据属性文件设置更改应用程序的值。这可能吗?应用程序如何访问 facelets.development?
我认为最简单的方法是将 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(“简介”和“上下文参数”部分)。
我可以想到几种方法来做到这一点,但都不是很愉快。
我会赞成其他一些方法。如果在测试机器上需要此设置,则部署脚本可能会在安装这些服务器的过程中修改应用程序。如果您希望在源代码管理中将其设置为 true,则构建脚本可以将其作为构建过程的一部分删除。这种方法不会影响您的运行时代码。
我已经对上面的选项 1 实施了一个变体。例如
代理看起来像:
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>
* <!-- Enables special Facelets debug output during development -->
* <context-param>
* <param-name>facelets.DEVELOPMENT</param-name>
* <param-value>true</param-value>
* </context-param>
* </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() 看起来像是另一天的练习,但它只返回应用程序应该使用的属性值映射