Atom Feed SITE FEED   ADD TO GOOGLE READER

Thoughts on Java as the configuration language for Guice

Google Guice uses plain old Java code as the configuration code. For example, here's the code that sets up my fictional pizza store management application:
public class PizzaOrdering implements Module {
public void configure(Binder binder) {
// tell Guice that whenever the code needs an implementation of
// my CallInOrderHandler interface, the WebCallInOrderHandler
// shall be used
binder.bind(CallInOrderHandler.class).to(WebCallInOrderHandler.class);

// tell Guice that whenever the code needs an OrderEditor, they
// should be provided with the same WebOrderEditor for the duration
// of their current phone call
binder.bind(OrderEditor.class)
.to(WebOrderEditor.class)
.in(PhoneCallScope.class)
}
}

To make it more enterprisey, a highly paid consultant could probably create an XML language to represent the same thing:
<binding key="com.publicobject.pizza.CallInOrderHandler" 
target="com.publicobject.pizza.ui.WebCallInOrderHandler">
<binding key="com.publicobject.pizza.OrderEditor"
target="com.publicobject.pizza.ui.WebOrderEditor"
scope="com.publicobject.pizza.request.PhoneCallScope">

There's are plenty of arguments for and against XML for configuration. I prefer to use Java code because it's compile-time checked and the syntax is concise. But recently I've found a new and interesting problem with this approach,
The only way for tools to read the configuration is to execute it.
Consider this module:
public class PizzaDelivery implements Module {
public void configure(Binder binder) {
String strategy = System.getProperty("DeliveryDriverQueueingStrategy");
if ("FirstInFirstOut".equals(strategy)) {
binder.bind(DeliveryDriverQueueingStrategy.class)
.to(FifoDeliveryDriverQueueingStrategy.class);
} else if ("FirstInLastOut".equals(strategy)) {
binder.bind(DeliveryDriverQueueingStrategy.class)
.to(FiloDeliveryDriverQueueingStrategy.class);
} else {
logger.warn("No queueing strategy!");
}

This is way more flexible than XML. But it's also less approachable by tools because it cannot be analyzed statically. Without executing PizzaDelivery (with the proper environment!), the tool cannot answer "what is DeliveryDriverQueueingStrategy bound to?", or even "Is DeliveryDriverQueueingStrategy bound?"

Is this limitation fatal? Certainly not. Most of the Modules I've seen contain trivial control flow and can easily be analyzed by tools. In rare cases, the configuration can be recorded as the module is executed, either alone or as part of a running application.

How can we make our Guice code tool-friendly?
  • Try to keep your Modules simple and free of control flow.
  • Avoid bind(Service.class).toInstance(new ServiceImpl()), which is less toolable than bind(Service.class).to(ServiceImpl.class). It also means that ServiceImpl doesn't need to be constructed if ever the Module is executed by a tool.
  • I only want to pipe up to say that this was fully intentional! :)

    Thanks for giving out great advice; I hope it will be followed.