Çağatay approached JSF/Guice integration by using a ServletContextListener to initialize Guice while annotating each managed bean with @PostConstruct. For my spike, I have kept the ServletContextListener but replaced @PostConstruct with a custom VariableResolver. It goes like this ...
The Model
public class ShoppingCart {
private Order order;
@Inject
public void setOrder(Order order) { this.order = order; }
public Order getOrder() { return order; }
}
public interface Order { }
public class BulkOrder implements Order {
public String toString() { return "I am a Bulk Order"; }
}The View
<html>This page renders "I am a Bulk Order", even though the shopping cart has no notion of a bulk order. The bulk order is injected into the shopping cart instance, a regular managed bean.
<body>
<f:view>
<h:outputText value="#{shoppingCart.order}" />
</f:view>
</body>
</html>
<managed-bean>This isn't an escape from XML hell, but it will put your configuration files on a diet. You still have to "name" each managed bean, Guice handles everything else.
<managed-bean-name>shoppingCart</managed-bean-name>
<managed-bean-class>com.thoughtworks.guice.ShoppingCart
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
How it Works
Once JSF has created the ShoppingCart instance, Guice is allowed to intercept before it is received by the view. (In faces-config.xml )<application>The GuiceVariableResolver relies on the GuiceServletContextListener, which isn't any different from Çağatay's ServletContextListener (In web.xml)
<variable-resolver>
com.thoughtworks.guice.GuiceVariableResolver
</variable-resolver>
</application>
public class GuiceVariableResolver extends VariableResolver {
private final VariableResolver wrapped;
public GuiceVariableResolver(VariableResolver wrapped) {
if(wrapped == null)
throw new NullPointerException("wrapped "
+ VariableResolver.class.getName());
this.wrapped = wrapped;
}
@Override
public Object resolveVariable(FacesContext fctx, String name)
throws EvaluationException {
Object resolved = wrapped.resolveVariable(fctx, name);
if(resolved != null) {
Mapmap = fctx.getExternalContext().getApplicationMap();
Injector injector = (Injector) map.get(Injector.class.getName());
if(injector == null)
throw new NullPointerException("Could not locate "
+ "Guice Injector in application scope using"
+ " key '" + Injector.class.getName() + "'");
injector.injectMembers(resolved);
}
return resolved;
}
}
<listener>
<listener-class>
com.thoughtworks.guice.GuiceServletContextListener
</listener-class>
</listener>
public class GuiceServletContextListener
implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
ServletContext ctx = event.getServletContext();
Injector inject = Guice.createInjector(new ShoppingModule());
ctx.setAttribute(Injector.class.getName(), inject);
}
public void contextDestroyed(ServletContextEvent event) {
ServletContext ctx = event.getServletContext();
ctx.removeAttribute(Injector.class.getName());
}
}
public class ShoppingModule implements Module {
public void configure(Binder binder) {
binder.bind(Order.class).to(BulkOrder.class);
}
}

5 comments:
Nice hack Dennis, better jsf 1.1 support for sure:)
Thanks man, here is part 2 ... http://notdennisbyrne.blogspot.com/2007/10/integrating-guice-and-jsf-part-2.html
7Lp3tH Magnific!
I thing this is a good solution...
http://code.google.com/p/guicesf/downloads/list
Implementing GuiceVariableResolver + adding it to faces-config.xml was enough to make it work for us! We use Guice 3.0 / MyFaces 1.1. We didn't touch any other class. Thank you!!!
Post a Comment