- The examples were too trivial to extend to real applications.
- The more complicated examples were doing really horrid things. (Like a singleton class that keeps the injector available for lazy and even not-so-lazy instantiations. Sorry, but why is this better? That's like taking an orange-overall crook, dressing him in a suit and calling him an honest politician. Uhh... wait... that's not what I mean...)
- Guice does not lend itself well to already badly written code, hence the difficulty to find examples that deals with those situations.
- You have one injector and one injector only. Inject once and throw it away. The only exception I make to this is a cool trick in anonymous inner classes (more on this, later).
- Keep the modules few, and put them in the same package as the program entry point. It is after-all the place where the module is used.
- Do not go too far. You can easily over-guice and this DOES make matters worse.
- Do not force Guice down the throat the people that will make use of your code (i.e. if you are writing a library). There is enough of that in the computer world.
Regardless, in order to get this working another injector needs to be passed to the servlet, violating my first rule. However... I found a workaround. I'll let the code speak for itself. Here goes:
First the main class where everything gets wired up. Simple and self-explanatory:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package gj.guice.xmlrpc; | |
import org.eclipse.jetty.server.Server; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import com.google.inject.Guice; | |
import com.google.inject.Inject; | |
import com.google.inject.Injector; | |
public class SimpleServer { | |
private Logger logger = LoggerFactory.getLogger(SimpleServer.class); | |
private static final int PORT = 8081; | |
private Server server; | |
@Inject | |
public SimpleServer(Server server) { | |
this.server = server; | |
} | |
public void start() throws Exception { | |
server.start(); | |
logger.debug("Server started on port: " + PORT); | |
} | |
public static void main(String[] args) throws Exception { | |
Injector injector = Guice.createInjector(new SimpleModule(PORT)); | |
SimpleServer server = injector.getInstance(SimpleServer.class); | |
server.start(); | |
} | |
} |
Secondly, the servlet. This merely redirects traffic to the handler and sets the factory. Note the injection. Also note the absense of an injector:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package gj.guice.xmlrpc; | |
import java.io.IOException; | |
import javax.servlet.ServletException; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import org.apache.xmlrpc.XmlRpcException; | |
import org.apache.xmlrpc.XmlRpcHandler; | |
import org.apache.xmlrpc.server.PropertyHandlerMapping; | |
import org.apache.xmlrpc.server.RequestProcessorFactoryFactory; | |
import org.apache.xmlrpc.server.XmlRpcHandlerMapping; | |
import org.apache.xmlrpc.server.XmlRpcNoSuchHandlerException; | |
import org.apache.xmlrpc.webserver.XmlRpcServlet; | |
import com.google.inject.Inject; | |
public class RedirectXmlRpcServlet extends XmlRpcServlet { | |
private static final long serialVersionUID = 1L; | |
private volatile PropertyHandlerMapping mapping = new PropertyHandlerMapping(); | |
@Inject | |
public RedirectXmlRpcServlet(RequestProcessorFactoryFactory factory) { | |
mapping.setRequestProcessorFactoryFactory(factory); | |
} | |
@Override | |
protected XmlRpcHandlerMapping newXmlRpcHandlerMapping() throws XmlRpcException { | |
return new XmlRpcHandlerMapping() { | |
@Override | |
public XmlRpcHandler getHandler(String operation) throws XmlRpcNoSuchHandlerException, XmlRpcException { | |
Class<?> clazz = MyHandler.class; | |
mapping.addHandler(clazz.getSimpleName(), MyHandler.class); | |
return mapping.getHandler(clazz.getSimpleName() + ".request"); | |
} | |
}; | |
} | |
@Override | |
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { | |
super.doGet(request, response); | |
} | |
@Override | |
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { | |
super.doPost(request, response); | |
} | |
} |
Thirdly the handler with some test injection (demonstrating that we can inject anything we want). It merely echoes the request:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package gj.guice.xmlrpc; | |
import java.util.Map; | |
import org.apache.xmlrpc.XmlRpcException; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import com.google.inject.Inject; | |
import com.google.inject.name.Named; | |
public class MyHandler { | |
private final Logger logger = LoggerFactory.getLogger(getClass()); | |
@Inject | |
public MyHandler(@Named("mystring") String value) { | |
logger.debug("Injected value: {}", value); | |
} | |
public Map<String, Object> request(Map<String, Object> in) throws XmlRpcException { | |
logger.debug("Request received..."); | |
return in; | |
} | |
} |
...and lastly, the module. The magic happens in the getRequestProcessorFactoryFactory provider. Have you ever had the need to write an anonymous local inner class within yet another anonymous local inner class? Well, now you do. I call it the Russion-doll pattern (anti-pattern?) Guice has access to the injector so you can merely inject it here as well:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package gj.guice.xmlrpc; | |
import org.apache.xmlrpc.XmlRpcException; | |
import org.apache.xmlrpc.XmlRpcRequest; | |
import org.apache.xmlrpc.server.RequestProcessorFactoryFactory; | |
import org.eclipse.jetty.server.Connector; | |
import org.eclipse.jetty.server.Server; | |
import org.eclipse.jetty.server.nio.SelectChannelConnector; | |
import org.eclipse.jetty.servlet.ServletContextHandler; | |
import org.eclipse.jetty.servlet.ServletHolder; | |
import com.google.inject.Binder; | |
import com.google.inject.Injector; | |
import com.google.inject.Module; | |
import com.google.inject.Provides; | |
import com.google.inject.name.Names; | |
public class SimpleModule implements Module { | |
private final int port; | |
public SimpleModule(int port) { | |
this.port = port; | |
} | |
@Override | |
public void configure(Binder binder) { | |
binder.bind(MyHandler.class); | |
binder.bindConstant().annotatedWith(Names.named("mystring")).to("See it works!"); | |
} | |
@Provides | |
public Server getServer(final ServletContextHandler handler) { | |
Server server = new Server(); | |
Connector connector = new SelectChannelConnector(); | |
connector.setHost("0.0.0.0"); | |
connector.setPort(port); | |
server.setConnectors(new Connector[] { connector }); | |
server.setHandler(handler); | |
return server; | |
} | |
@Provides | |
public ServletContextHandler getServletHandler(RequestProcessorFactoryFactory factory) { | |
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); | |
context.setContextPath("/"); | |
context.addServlet(new ServletHolder(new RedirectXmlRpcServlet(factory)), "/*"); | |
return context; | |
} | |
@Provides | |
@SuppressWarnings("unchecked") | |
public RequestProcessorFactoryFactory getRequestProcessorFactoryFactory(final Injector injector) { | |
return new RequestProcessorFactoryFactory() { | |
@Override | |
public RequestProcessorFactory getRequestProcessorFactory(final Class pClass) throws XmlRpcException { | |
return new RequestProcessorFactory() { | |
@Override | |
public Object getRequestProcessor(XmlRpcRequest pRequest) throws XmlRpcException { | |
return injector.getInstance(pClass); | |
} | |
}; | |
} | |
}; | |
} | |
} |
So, the order of creation. The main class gets an instance of the SimpleServer, which needs an instance of a ServletContextHandler, which needs a RequestProcessorFactoryFactory which needs an injector to inject an instance of a new handler. Basically boils down to lazy instantiation which is set up in the module.