Connecting JBoss CXF Web Service with a WCF Client

Posted: March 13, 2009 in geek stuff
Tags: , , , , ,

I have been trying for a few days to get a JBoss web service using reliable messaging and ws addressing to talk to an M$ client. There are lots of useful tuts on the topic, but nothing that showed all the bits which I needed to set up. I have it working, so I figured I would post the important bits for other folks to see.

I am using the following stacks:
JBoss 5.0.1 running on Fedora 10
Jbossws-CXF 3.1.0
Visual Studio 2008 running on Server 2003

The combination which seems to work is
Soap 1.1 or 1.2
Reliable Messaging 1.0 (February 2005 version)
Addressing 1.0 (August 2004 version)

My Service is a standard EJB using annotaitons to link to the handcrafted wsdl. It seems that you need to use handcrafted wsdl to do anything interesting. Here is the web service definition:

package com.redhat.vdc.backend;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.wsf.spi.annotation.WebContext;

import com.redhat.vdc.action.VdcActionParameters;
import com.redhat.vdc.action.VdcActionType;
import com.redhat.vdc.action.VdcReturnValue;
import com.redhat.vdc.common.utils.SessionContainer;
import com.redhat.vdc.common.utils.VdcException;

@WebService(serviceName = "WCFBackendBean", wsdlLocation = "META-INF/wsdl/com.redhat.vdc.backend.WCFBackendBean.wsdl")
@WebContext(contextRoot = "/backends", urlPattern = "wcf")
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
@Stateless(name = "WCFBackend")
public class WCFBackendBean extends VdcAdapter {

	@EJB(beanName = "Backend")
	Backend backend;

	Log log = LogFactory.getLog(WCFBackendBean.class);


	@WebMethod
	@WebResult(name = "vdcReturnValue")
	public VdcReturnValue runAction(
			@WebParam(name = "actionType") VdcActionType type,
			@WebParam(name = "parameters") VdcActionParameters params) {

		try {
			this.initializeSession() ;
			this.incrementRequestCounter() ;
			log.debug("Request Number: " + SessionContainer.getInstance().getData("requestCount")) ;
			return backend.runAction(type, params);
		} catch (VdcException e) {
			throw e;
		} catch (Exception e) {
			throw new VdcException(e);
		} finally {
			resetSession() ;
		}
	}

}

The WSDl is located in the META-INF directory of the EJB jar. It defines the standards listed above:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://backend.vdc.redhat.com/"
	name="BareBackendBean" xmlns:tns="http://backend.vdc.redhat.com/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:soap11="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:soap12="http://java.sun.com/xml/ns/jaxws/2003/05/soap/bindings/HTTP/"
    xmlns:soap12CXF="http://schemas.xmlsoap.org/wsdl/soap12/"
	xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
	xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy" 
	xmlns:wsrmp="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"
	xmlns:wsrmp11="http://docs.oasis-open.org/ws-rx/wsrmp/200702"
	xmlns:wsrmp10ForJavaNative="http://schemas.xmlsoap.org/ws/2005/02/rm"
	xmlns:wsrmp10="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"
	xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
	xmlns="http://schemas.xmlsoap.org/wsdl/">
	<types>
		<xsd:schema>
			<xsd:import namespace="http://backend.vdc.redhat.com/"
				schemaLocation="WCFBackendBean_schema1.xsd" />
		</xsd:schema>
	</types>
	<message name="runAction">
		<part name="parameters" element="tns:runAction" />
	</message>
	<message name="runActionResponse">
		<part name="parameters" element="tns:runActionResponse" />
	</message>
    <portType name="BareBackendBean">
        <operation name="runAction">
            <input
                wsaw:Action="http://backend.vdc.redhat.com/BareBackendBean/runActionRequest"
                message="tns:runAction">
            </input>
            <output message="tns:runActionResponse">
            </output>
        </operation>
    </portType>
	<binding name="BareBackendBeanPortBinding" type="tns:BareBackendBean">
		<wsaw:UsingAddressing />
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
			style="document" />
		<operation name="runAction">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
	</binding>
	<service name="BareBackendBean">
		<port name="BareBackendBeanPort" binding="tns:BareBackendBeanPortBinding">
			<soap:address location="http://127.0.0.1:8080/backends/bare" />
		</port>
	</service>
</definitions>

Also in the META-INF directory is a jboss-cxf.xml file which has the following contents. Again, this references the versions of the WS standards listed above:

<beans xmlns='http://www.springframework.org/schema/beans'
	xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' 
	xmlns:beans='http://www.springframework.org/schema/beans'
	xmlns:jaxws='http://cxf.apache.org/jaxws' 
	xmlns:wsp='http://www.w3.org/2006/07/ws-policy'
	xmlns:p='http://cxf.apache.org/policy'
	xsi:schemaLocation='http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://cxf.apache.org/policy
    http://cxf.apache.org/schemas/policy.xsd 
    http://www.w3.org/2006/07/ws-policy
    http://www.w3.org/2006/11/ws-policy.xsd 
    http://cxf.apache.org/jaxws
    http://cxf.apache.org/schemas/jaxws.xsd'>

    <bean id="logInbound" class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
    <bean id="logOutbound" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>




	<wsp:Policy wsu:Id="wsrm10policy"
		xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
		<wswa:UsingAddressing xmlns:wswa="http://www.w3.org/2006/05/addressing/wsdl" />
		<wsrmp:RMAssertion xmlns:wsrmp="http://schemas.xmlsoap.org/ws/2005/02/rm/policy" >
            		<wsrmp:AcknowledgementInterval Milliseconds="1000"/> 		
		</wsrmp:RMAssertion>
	</wsp:Policy>

	<jaxws:endpoint id='WCFBackend'
		implementor='com.redhat.vdc.backend.WCFBackendBean'>
		<jaxws:invoker>
			<bean class='org.jboss.wsf.stack.cxf.InvokerJSE' />
		</jaxws:invoker>
		<jaxws:features>
			<p:policies namespace="http://www.w3.org/2006/07/ws-policy">
				<wsp:PolicyReference URI="#wsrm10policy"
					xmlns:wsp="http://www.w3.org/2006/07/ws-policy" />
			</p:policies>
		</jaxws:features>
		<jaxws:outInterceptors>
		    <ref bean="logOutbound"/>
		</jaxws:outInterceptors>
		<jaxws:inInterceptors>
		    <ref bean="logInbound"/>
		</jaxws:inInterceptors>		
	</jaxws:endpoint>
	
</beans>

On the client side, I used the visual studio tool to create a reference to th web service. By default, I got a basicHttpBindings in the app.config file. The voodoo was to replace the basic binding with a custom binding that references the same WS standards. Here is the file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <customBinding>
                <binding name="WCFBackendBeanPortBinding1">
                    <textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
                        messageVersion="Soap11WSAddressingAugust2004" writeEncoding="utf-8">
                        <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    </textMessageEncoding>
                  <reliableSession ordered="true" reliableMessagingVersion="WSReliableMessagingFebruary2005" flowControlEnabled="true" inactivityTimeout="00:15:00" />                  
                    <httpTransport manualAddressing="false" maxBufferPoolSize="524288"
                        maxReceivedMessageSize="65536" allowCookies="true" authenticationScheme="Anonymous"
                        bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                        keepAliveEnabled="true" maxBufferSize="65536" proxyAuthenticationScheme="Anonymous"
                        realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"
                        useDefaultWebProxy="true" />
                </binding>
            </customBinding>
        </bindings>
        <client>
            <endpoint address="http://hattrick.usersys.redhat.com:8080/backends/wcf"
                binding="customBinding" bindingConfiguration="WCFBackendBeanPortBinding1"
                contract="ServiceReferenceWCF.WCFBackendBean" name="WCFBackendBeanPort1" />
        </client>
    </system.serviceModel>
</configuration>

Deploy the ejb, compile the front end, and it works. The same endpoint can be use by a CXF client, so there is good interoperability between java and C#.

Advertisements
Comments
  1. robob says:

    Have you ever had problems with webmethod returning a string array (string[]) or int array (int[])?

    I have just implemented a WCF client to a JBossWS and only the webmethods returning the string[] and int[] don’t work. The exception in C# is an “ArgumentException”.

    Very strange because another webmethod return my bean array and all works fine!

    • bryankearney says:

      I did with an array of objects. I ended up having to create a concrete subclass to hold the array of objects. I would dig into the JAXB stuff to see if there are any annotations which improve the marshalling.

  2. robob says:

    Here is the post in the MSDN Forum where I explain the problem with all the details. The exception is very strange and it seems depending only from the client side (I think).

    http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/0a48d50d-64d1-43b1-be05-d1b15643223f/

    I think to do an example with a sample WS client and WS in JBoss where the problem is replicated and sharing it with everbody.

    The problem I have is too strange! And I’m beginning to think it is a my mistake.

    I have to admit that I am a newbie in WCF and JBOSSWS too 🙂

  3. robob says:

    The problem is not related to the arrays but a mistake in the deserialization of the SOAP message for the missing attribute in the ‘System.Xml.Serialization.XmlArrayAttribute’.

    I have to say that WCF mapping of a WSDL exported from JBossWS is not very good and probably we are pioneers in this field. What do you think about the error? Have you had the time to see the post I updateed?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s