tags:

views:

163

answers:

3

EDIT: the issue was with the [MessageHeader] attributes in the ResponseMessage class; Metro/JAX-WS doesn't seem capable of handling these attributes. Changing them to [MessageBodyMember] solved the issue.


As the title says, I need to get some Java 1.5 code to call a WCF web service. I've downloaded and used Metro to generate Java proxy classes, but they aren't generating what I expect, and I believe this is because of the WSDL that the WCF service generates.

My WCF classes look like this (full code omitted for brevity):

public class TestService : IService
{
  public TestResponse DoTest(TestRequest request)
  {
    TestResponse response = new TestResponse();

    // actual testing code...

    response.Result = ResponseResult.Success;

    return response;
  }
}

public class TestResponse : ResponseMessage
{
  public bool TestSucceeded { get; set; }
}

public class ResponseMessage
{
  [MessageHeader]
  public ResponseResult Result { get; set; }

  [MessageHeader]
  public string ResponseDesc { get; set; }

  [MessageHeader]
  public Guid ErrorIdentifier { get; set; }
}

public enum ResponseResult
{
  Success,
  Error,
  Empty,
}

and the resulting WSDL (when I browse to http://localhost/TestService?wsdl=wsdl0) looks like this:

<xsd:element name="TestResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element minOccurs="0" name="TestSucceeded" type="xsd:boolean" /> 
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ErrorIdentifier" type="q1:guid" xmlns:q1="http://schemas.microsoft.com/2003/10/Serialization/" /> 
<xsd:simpleType name="ResponseResult">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Error" /> 
<xsd:enumeration value="Success" /> 
<xsd:enumeration value="EmptyResult" /> 
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="ResponseResult" nillable="true" type="tns:ResponseResult" /> 
<xsd:element name="Result" type="tns:ResponseResult" /> 
<xsd:element name="ResultDesc" nillable="true" type="xsd:string" />

...

<xs:element name="guid" nillable="true" type="tns:guid" /> 
<xs:simpleType name="guid">
<xs:restriction base="xs:string">
<xs:pattern value="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}" /> 
</xs:restriction>
</xs:simpleType>

Immediately I see an issue with this WSDL: TestResponse does not contain the properties inherited from ResponseMessage. Since this service has always worked in Visual Studio I've never questioned this before, but maybe that could be causing my problem?

Anyhow, when I run Metro's wsimport.bat on the service the following error message is generated:

[WARNING] src-resolve.4.2: Error resolving component 'q1:guid'

and the outputted Java version of TestResponse lacks any of the properties from ResponseMessage.

I hacked the WSDL a bit and changed ErrorIdentifier to be typed as xsd:string, which makes the message about resolving the GUID type go away, but I still don't get any of ResponseMessage's properties.

Finally, I altered the WSDL to include the 3 properties from ResponseMessage in TestResponse, and of course the end result is that the generated .java file contains them. However, when I actually call the WCF service from Java, those 3 properties are always null.

Any advice, apart from writing the proxy classes myself?

A: 

This problem has nothing to do with Java, but only with the WSDL being generated.

Why are you not using [DataContract] and [DataMember] on your TestResponse and ResponseMessage classes? Try that and see if it works.

John Saunders
All the requisite attributes are present, but I omitted them to make my post shorter. As my answer shows, that was probably a bad idea.
Ian Kemp
A: 

Hm, I do not mean to be glib or reject Java-native solutions out of hand, but when it comes to the Wcf stack, the best advice is typically to use the Wcf stack.

More to the point: generate a CLR Wcf client [in language of choice auto-generate client via VS or svcutil against static wsdl or service endpoint] and then invoke this CLR proxy through some Java-CLR interop.

There are several reasons for this, not the least of which

  • Immediate compliance [by Wcf for Wcf]
  • Flexibility [it is not unreasonable to expect your remote service to change or leverage sophisticated communication protocols, such as message-level encryption, you want your client to change as easily as the service]

ps: I have strong misgivings about modifying existing service wsdls by hand. If the wsdl is indeed "the problem", then this indicates either a problem with service definition [ie server-side code] or service interpretation [ie an option on autogeneration utility]. Avoid manual manipulations of service contracts. Doing so would be like modifying IL byte code post-build.

johnny g
I'm completely with you regarding editing WSDL to make things work - it's just that I was running out of time and options and was willing to try anything to make things work. :)
Ian Kemp
@Ian Kemp, i hear ya ;)
johnny g
I don't agree we have got both java calling wcf and wcf calling java web service with both message security and two-way ssl security working very successfully (without modifying WSDL). This is for real multi-million dollar commercial scenario. Metro is the best java stack to interoperate with WCF services.
Pratik
+3  A: 

Some #%$^@! changed the [MessageBodyMember] attributes on the properties of ResponseMessage to [MessageHeader] without telling anyone. I changed them back to [MessageBodyMember], regenerated the proxy classes and everything works correctly.

Onced I've finished the integration I'll be doing CVS diffs to find the person responsible, and then they shall SUFFER MY WRATH.

Ian Kemp
@Ian Kemp, Svn Blame !!!
johnny g
@Ian: glad you found the problem; +1 for posting it. Please update your question to show the true attributes, otherwise it will confuse future readers. You should also accept your own answer.
John Saunders