Apache CXF 2.0 Documentation > Index > Service Routing |
One thing you'll often need to do when developing services, is to develop a new version while keeping the old version running. This guide shows how to develop a simple service router that will scan the incoming message then direct the message to the appropriate service.
One common practice to version web services is using XML namespaces to clearly delineate the versions of a document that are compatible. For example:
<wsdl:types> <schema targetNamespace="http://apache.org/2007/03/21/hello_world_xml_http/mixed/types" xmlns="http://www.w3.org/2001/XMLSchema"> <element name="sayHi"> <complexType /> </element> .......... </schema> </wsdl:types>
Among many different possible implementations of service routing, one simple way ("simple" in terms of amount of codes you have to write, but it does require a certain extent of familiarity with CXF internal architectures) to do this is writing a CXF interceptor that acts as a routing mediator.
Firstly we need to have a dummy service with an intermediary interceptor registered. This intermediary interceptor is positioned at the very beginning of the interceptor chain, which is to make sure the intermediary interceptor will be the first interceptor being invoked in the message pipeline. The intermediary interceptor will scan the incoming message for example, detect the schema namespace, then direct the message to the desired endpoint according to user programmed strategy.
Lets see the code:
import javax.xml.ws.Endpoint; import org.apache.cxf.jaxws.EndpointImpl; import org.apache.cxf.testutil.common.AbstractBusTestServerBase; import org.apache.hello_world_mixedstyle.GreeterImplMixedStyle; public class Server extends AbstractBusTestServerBase { protected void run() { Object implementor1 = new GreeterImplMixedStyle(); String address1 = "http://localhost:9027/SoapContext1/SoapPort"; Endpoint.publish(address1, implementor1); Object implementor2 = new GreeterImplMixedStyle(); String address2 = "http://localhost:9027/SoapContext2/SoapPort"; Endpoint.publish(address2, implementor2); //A dummy service that acts as a routing mediator Object implementor = new GreeterImplMixedStyle(); String address = "http://localhost:9027/SoapContext/SoapPort"; javax.xml.ws.Endpoint jaxwsEndpoint = Endpoint.publish(address, implementor); //Register a MediatorInInterceptor on this dummy service EndpointImpl jaxwsEndpointImpl = (EndpointImpl)jaxwsEndpoint; org.apache.cxf.endpoint.Server server = jaxwsEndpointImpl.getServer(); org.apache.cxf.endpoint.Endpoint endpoint = server.getEndpoint(); endpoint.getInInterceptors().add(new MediatorInInterceptor()); } public static void main(String[] args) { try { Server s = new Server(); s.start(); } catch (Exception ex) { ex.printStackTrace(); System.exit(-1); } finally { System.out.println("done!"); } } }
import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.logging.Logger; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.apache.cxf.Bus; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.binding.soap.SoapVersion; import org.apache.cxf.binding.soap.SoapVersionFactory; import org.apache.cxf.bus.CXFBusFactory; import org.apache.cxf.endpoint.Server; import org.apache.cxf.endpoint.ServerRegistry; import org.apache.cxf.interceptor.InterceptorChain; import org.apache.cxf.interceptor.StaxInInterceptor; import org.apache.cxf.message.Message; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.apache.cxf.staxutils.DepthXMLStreamReader; import org.apache.cxf.staxutils.StaxUtils; import org.apache.cxf.transport.MessageObserver; public class MediatorInInterceptor extends AbstractPhaseInterceptor<SoapMessage> { private static final Logger LOG = Logger.getLogger(MediatorInInterceptor.class.getName()); public MediatorInInterceptor() { super(); setPhase(Phase.POST_STREAM); addBefore(StaxInInterceptor.class.getName()); } public void handleMessage(SoapMessage message) { if (isGET(message)) { LOG.info("StaxInInterceptor skipped in HTTP GET method"); return; } String schemaNamespace = ""; InterceptorChain chain = message.getInterceptorChain(); try { //create a buffered stream so that we can roll back to original stream after finishing scaning InputStream is = message.getContent(InputStream.class); BufferedInputStream pis = new BufferedInputStream(is); pis.mark(pis.available()); message.setContent(InputStream.class, pis); //TODO: need to process attachements //Scan the schema namespace, which is used to indicate the service version String encoding = (String)message.get(Message.ENCODING); XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(pis, encoding); DepthXMLStreamReader xmlReader = new DepthXMLStreamReader(reader); if (xmlReader.nextTag() == XMLStreamConstants.START_ELEMENT) { String ns = xmlReader.getNamespaceURI(); SoapVersion soapVersion = SoapVersionFactory.getInstance().getSoapVersion(ns); StaxUtils.toNextTag(xmlReader, soapVersion.getBody()); // advance just past body. xmlReader.nextTag(); } schemaNamespace = xmlReader.getName().getNamespaceURI(); //Roll back to the original inputStream pis.reset(); } catch (IOException e) { e.printStackTrace(); } catch (XMLStreamException e) { e.printStackTrace(); } Bus bus = CXFBusFactory.getDefaultBus(); ServerRegistry serverRegistry = bus.getExtension(ServerRegistry.class); List<Server> servers = serverRegistry.getServers(); Server targetServer = null; for (Server server : servers) { targetServer = server; String address = server.getEndpoint().getEndpointInfo().getAddress(); if (schemaNamespace.indexOf("2007/03/21") != -1) { if (address.indexOf("SoapContext2") != -1) { break; } } else if (address.indexOf("SoapContext1") != -1) { break; } } //Redirect the request MessageObserver ob = targetServer.getMessageObserver(); ob.onMessage(message); chain.abort(); } }
A couple of things to note: