...
Requirements
We should support to deploy cxf client(as service consumer) and cxf server (as service provider) easilly into servicemix.
We need a cxf service engine where we can deploy cxf client and server into. As a service engine (SE) provides some type of logic inside the JBI environment and only communicates with the NMR. If a Cxf SE needs to communicate outside the JBI environment (e.g. the client inside servicemix but server outside servicemix or vice verse), it must send a message to a binding component (via the NMR), so we also need a cxf binding component to bridge the incoming/outgoing message. And Also we should leverage ws-* feature of CXF by cxf binding component.
key
...
components
cxf service engine
overview
...
This class just extends servicemix DefaultComponent
Which is JBI spec compitable component. By extends extending servicemix DefaultComponent, we can only focus cxf specific issue for our cxf service engine since all JBI spec details is handled by servicemix DefaultComponent. Here we just init cxf bus when CxfSeComponent init.
...
- JBI compliant Binding Component
- Usable in a lightweight mode in servicemix.xml configuration files
- SOAP 1.1 and 1.2 support
- MIME attachments
- Support for InOut or InOnly MEPs as consumers or providers
- SSL support
- WS-Security support
- WS-Policy support
- WS-RM support
- WS-Addressing support
...
- cxf server
- servicemix consumer
As cxf server, just init a dummy server without real service class. This cxf server init the proper transport(http or jms) from the service model which is built from the wsdl. Since there is no service class for this server, this server will do no real service invocation, just transform message format between soap binding and jbi binding.
For a request message, need transform soap message to jbi message, two main interceptors get involved
JbiInInterceptor
JbiInInterceptor mainly create JBI NormalizedMessage and copy headers and attachments from soap message to JBI message
JbiInWsdl1Interceptor
JbiInWsdl1Interceptor transform soap messasge to jbi message according to the service model
For a reponse message, need transform jbi message to soap message, also two main interceptors get involved
JbiOutInterceptor
JbiOutInterceptor mainly copy attachments and headers from jbi message to soap message
JbiOutWsdl1Interceptor
JbiOutWsdl1Interceptor transform jbi mesage to soap message based on the service model
As servicemix consumer, send jbi message which transformed from the soap message to NMR and need process the response jbi message from the NMR.
CxfBcConsumer use its own JbiInvokerInterceptor to wire these two role together.
Main part of JbiInvokerInterceptor
Also the code for processing response jbi message looks likeCode Block lang java ublic class JbiInvokerInterceptor extends AbstractPhaseInterceptor<Message> { public JbiInvokerInterceptor() { super(Phase.INVOKE); } public void handleMessage(final Message message) throws Fault { final Exchange cxfExchange = message.getExchange(); final Endpoint endpoint = cxfExchange.get(Endpoint.class); final Service service = endpoint.getService(); final Invoker invoker = service.getInvoker(); MessageExchange exchange = message .getContent(MessageExchange.class); ComponentContext context = message.getExchange().get( ComponentContext.class); CxfBcConsumer.this.configureExchangeTarget(exchange); CxfBcConsumer.this.messages.put(exchange.getExchangeId(), message); CxfBcConsumer.this.isOneway = message.getExchange().get( BindingOperationInfo.class).getOperationInfo().isOneWay(); message.getExchange().setOneWay(CxfBcConsumer.this.isOneway); try { if (CxfBcConsumer.this.synchronous && !CxfBcConsumer.this.isOneway) { message.getInterceptorChain().pause();//just pause the incoming intercepor chain of cxf server, the mesage transformation from soap to jbi is done now context.getDeliveryChannel().sendSync(exchange, 10000timeout*1000);//send exchange which contain jbi message to NMR process(exchange); } else { context.getDeliveryChannel().send(exchange); } } catch (Exception e) { throw new Fault(e); } } }
Code Block java lang public void process(MessageExchange exchange) throws Exception { Message message = messages.remove(exchange.getExchangeId()); message.getInterceptorChain().resume();//resume the cxf outgoing interceptor chain, will do the message transformation from jbi to soap if (exchange.getStatus() == ExchangeStatus.ACTIVE) { exchange.setStatus(ExchangeStatus.DONE); message.getExchange().get(ComponentContext.class) .getDeliveryChannel().send(exchange); } }
CxfBcProvider
We use maven-xbean-plugin to generate provider xbean schema, so we need add comment to specify the xml element name firstly
/**first
Code Block | ||
---|---|---|
| ||
/** * * @org.apache.xbean.XBean element="provider" |
...
*/ |
Provider of binding component means bridge internal request to outside server, for example
cxf proxy inside servicemix ===> cxf bc provider===> standalone cxf server outside servicemix
Similiar as CxfBcConsumer, CxfBcProvider also play two role in the message process
- servicemix provider
- cxf client
The message process looks like
provider get incoming jbi message from NMR==> transform it to soap message==> send out to external service==> in the registered MessageObserver, get response soap message from external service==> transform it to jbi message==> send back to the NMR.
This process splitted into two classes,
The first half in CxfBcProviderCode Block lang java public void process(MessageExchange exchange) throws Exception { NormalizedMessage nm = exchange.getMessage("in");//get incoming jbi message CxfBcProviderMessageObserver obs = new CxfBcProviderMessageObserver(exchange, this); conduit.setMessageObserver(obs);// register message observer for cxf client Message message = ep.getBinding().createMessage();//create outgoing cxf message based on the service model, both soap11 and soap12 is supported message.put(MessageExchange.class, exchange); Exchange cxfExchange = new ExchangeImpl(); message.setExchange(cxfExchange); cxfExchange.setOutMessage(message); ... ... message.setInterceptorChain(outChain); InputStream is = JBIMessageHelper.convertMessageToInputStream(nm.getContent()); StreamSource source = new StreamSource(is); message.setContent(Source.class, source); message.setContent(InputStream.class, is); conduit.prepare(message); OutputStream os = message.getContent(OutputStream.class); XMLStreamWriter writer = message.getContent(XMLStreamWriter.class); String encoding = getEncoding(message); try { writer = StaxOutInterceptor.getXMLOutputFactory(message).createXMLStreamWriter(os, encoding); } catch (XMLStreamException e) { // } message.setContent(XMLStreamWriter.class, writer); message.put(org.apache.cxf.message.Message.REQUESTOR_ROLE, true); outChain.doIntercept(message);//transform jbi message to soap message XMLStreamWriter xtw = message.getContent(XMLStreamWriter.class); if (xtw != null) { xtw.writeEndDocument(); xtw.close(); } os.flush(); is.close(); os.close();//send out message to external service }
...
We have test cases almost cover all features of cxf bc and se, such as *
- proxy
...
- ws-policy
...
- ws-security
...
- ws-rm
...
- ws-addressing
...
- MTOM
...
- multiple transport support
...
- oneway
...
- Exception
Go through these tests for servicemix-cxf-se and servicemix-cxf-bcsome tips
what JBI binding message looks like
Code Block lang xml <jbi:message xmlns:jbi="http://java.sun.com/xml/ns/jbi/wsdl-11-wrapper" xmlns:msg="http://apache.org/cxf/calculator" name="add" type="msg:add" version="1.0"> <jbi:part> <add xmlns="http://apache.org/cxf/calculator/types"> <arg0>1</arg0> <arg1>2</arg1> </add> </jbi:part> </jbi:message>
what SOAP binding message looks like
Code Block lang xml <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Body> <add xmlns="http://apache.org/cxf/calculator/types"> <arg0>1</arg0> <arg1>2</arg1> </add> </soap:Body> </soap:Envelope>
can cxf component work with other component inside servicemix
Yes, provided those
...
- components can hanle the normalized JBI message.