Codehaus XFire
DocumentationQuicklinksDevelopers
Sponsors |
One thing you'll often need to do when developing services, is develop a new version while keeping the old version running. This is but one instance where you may want a service router of some sort. This guide shows how to develop a Handler which will read in a version header, and direct it to the appropriate service. Handlers are just interceptors which are able to be invoked before an after an endpoint is invoked. In this case where going to develop a handler which sits at the in flow. Handlers can participate in various "phases" of the message pipeline. In this case we're going to write a handler which is part of the pre-dispatch phase, meaning that it'll be invoked before we dispatch to our service. Lets see the code: import org.codehaus.xfire.MessageContext; import org.codehaus.xfire.fault.XFireFault; import org.codehaus.xfire.handler.AbstractHandler; import org.codehaus.xfire.handler.Phase; import org.codehaus.xfire.service.Service; import org.jdom.Element; import org.jdom.Namespace; public class ServiceRouterHandler extends AbstractHandler { public final static String VERSION_NS = "http://xfire.codehaus.org/examples/router"; public final static String VERSION_NAME = "Version"; public ServiceRouterHandler() { super(); setPhase(Phase.PRE_DISPATCH); } public void invoke(MessageContext context) throws Exception { Element header = context.getInMessage().getHeader(); if (header == null) return; Element versionEl = header.getChild(VERSION_NAME, Namespace.getNamespace(VERSION_NS)); if (versionEl == null) return; String version = versionEl.getValue(); if (version == null || version.length() == 0) { throw new XFireFault("An empty version element is not allowed.", XFireFault.SENDER); } setVersion(version, context); } /** * Looks up the appropriate service version using referenced by "Echo" plus the version string. */ private void setVersion(String version, MessageContext context) throws XFireFault { Service service = context.getXFire().getServiceRegistry().getService("Echo" + version); if (service == null) { throw new XFireFault("Invalid version: " + version, XFireFault.SENDER); } context.setService(service); } } Just a couple notes on something that should be pretty self explanatory:
Now lets look at a bit of set up. Here is a simple JUnit test. import org.codehaus.xfire.DefaultXFire; import org.codehaus.xfire.service.EchoImpl; import org.codehaus.xfire.service.Service; import org.codehaus.xfire.test.AbstractXFireTest; import org.jdom.Document; public class ServiceRouterTest extends AbstractXFireTest { Service serviceRouter; Service service1; Service service2; String service1Namespace = "http://xfire.codehaus.org/Echo1"; String service2Namespace = "http://xfire.codehaus.org/Echo2"; public void setUp() throws Exception { super.setUp(); // This is just an endpoint which doesn't really do anything serviceRouter = getServiceFactory().create(ServiceRouter.class); service1 = getServiceFactory().create(EchoImpl.class, "Echo1", service1Namespace, null); service2 = getServiceFactory().create(EchoImpl.class, "Echo2", service2Namespace, null); getServiceRegistry().register(serviceRouter); getServiceRegistry().register(service1); getServiceRegistry().register(service2); ((DefaultXFire) getXFire()).addInHandler(new ServiceRouterHandler()); } public void testInvoke() throws Exception { Document response = invokeService(serviceRouter.getSimpleName(), "/org/codehaus/xfire/examples/router/Echo2.xml"); addNamespace("m", "http://xfire.codehaus.org/Echo2"); assertValid("//m:echo", response); response = invokeService(serviceRouter.getSimpleName(), "/org/codehaus/xfire/examples/router/Echo1.xml"); addNamespace("m", "http://xfire.codehaus.org/Echo1"); assertValid("//m:echo", response); } public static interface ServiceRouter {} } Lets see whats going on here. First, we're creating a ServiceRouter service. We create this with just an empty interface. Technically this isn't needed in the test, but we need to establish an service for certain transports like HTTP where XFire expects a service name in the URL. Secondly, we're creating two versions of the Echo service in different namespaces. Then we register our three services. Finally, we add our ServiceRouterHandler to the list of global in handlers to be run. The testInvoke() method just tries out our handler for a spin. We send two different documents. Echo1.xml and Echo2.xml. Via simple XPath assertions we're able to tell we got the correct document back! |