I've implemented a variation on option 1 above. eg
- wrote a dynamic proxy for a ServletContext that intercepts the getInitParameter and getInitParameterNames methods returning appropriate values (sourced from environment-specific property files in this case)
- wrote a very small subclass of FacesContextFactoryImpl that proxies the first/servletcontext parameter to getFacesContext and then delegates to the superclass.
- added a faces-context-factory clause to my faces-config naming my FacesContextFactoryImpl class
- I already had a mechanism in place to load environment-specific property files and make them available as a Properties object to the application (the factory in point 2 passes these properties to the proxy in point 1 to use as an alternate source of initParameter values)
The proxy looks like:
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;
}
}
The factory looks like:
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);
}
}
and the faces-config looks like
<faces-config>
blah waffle....
<factory>
<faces-context-factory>zzzz.faces.context.FacesContextFactoryImpl</faces-context-factory>
</factory>
</faces-config>
What SystemContext.getInstance().getConfigProperties() looks like is an excercise for another day but it just returns the Map of property values the app is supposed to use