Calling a WCF service from Java
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 开发者_Python百科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?
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.
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.
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.
精彩评论