Details
-
Type:
Bug
-
Status:
Closed
-
Priority:
Critical
-
Resolution: Fixed
-
Affects Version/s: GroovyWS-0.5.0
-
Fix Version/s: GroovyWS-0.5.0
-
Component/s: GroovyWS
-
Labels:None
-
Environment:Windows XP, JDK 1.5.0_07, Groovy 1.6.0
Description
This is a description of multiple issues encountered, and workarounds employed while writing a script that uses GroovyWS to query the caDSR data service hosted by the National Cancer Institute. In addition to this writeup, the attached ZIP file contains scripts to help illustrate the issues and workarounds.
The caDSR data service is a public web service, with WSDL available at the following URL:
http://cadsr-dataservice.nci.nih.gov/wsrf/services/cagrid/CaDSRDataService?wsdl
My program needs to connect with the service from within grails, so I used the (tiny, ~25k) released groovyws-0.5.0.jar file available here:
http://repository.codehaus.org/org/codehaus/groovy/modules/groovyws/0.5.0/groovyws-0.5.0.jar
To resolve dependencies for the tiny groovyws-0.5.0.jar file, I separately obtained the other third-party JAR files listed on the "Using WSClient in Grails" wiki page:
http://groovy.codehaus.org/Using+WSClient+in+Grails
cxf-bundle-2.1.4.jar
wstx-asl-3.2.7.jar
commons-logging-1.1.1.jar
bcprov-jdk15-140.jar
XmlSchema-1.4.4.jar
FastInfoset-1.2.2.jar
jaxen-1.1.1.jar
jaxb-xjc-2.1.9.jar
jaxb-impl-2.1.9.jar
jaxb-api-2.1.jar
geronimo-ws-metadata_2.0_spec-1.1.2.jar
geronimo-stax-api_1.0_spec-1.0.1.jar
geronimo-jaxws_2.1_spec-1.0.jar
geronimo-javamail_1.4_spec-1.5.jar
geronimo-annotation_1.0_spec-1.1.1.jar
geronimo-activation_1.1_spec-1.0.2.jar
xmlsec-1.4.2.jar
xml-resolver-1.2.jar
xalan-2.7.1.jar
wss4j-1.5.6.jar
wsdl4j-1.6.2.jar
serializer-2.7.1.jar
saaj-impl-1.3.2.jar
saaj-api-1.3.jar
neethi-2.0.4.jar
jdom-1.1.jar
The Goal: Create a GroovyWS-based script that uses the CaDSRDataService to execute the equivalent of the following SOAP request and prints out the longName for each Form returned by the query.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dat="http://gov.nih.nci.cagrid.data/DataService" xmlns:gov="http://CQL.caBIG/1/gov.nih.nci.cagrid.CQLQuery">
<soapenv:Header/>
<soapenv:Body>
<dat:QueryRequest>
<dat:cqlQuery>
<gov:CQLQuery>
<gov:Target name="gov.nih.nci.cadsr.domain.Form">
<gov:Group logicRelation="AND">
<gov:Attribute name="workflowStatusName" predicate="EQUAL_TO" value="RELEASED"/>
<gov:Association name="gov.nih.nci.cadsr.domain.Context" roleName="context">
<gov:Attribute name="name" predicate="EQUAL_TO" value="NHLBI"/>
</gov:Association>
</gov:Group>
</gov:Target>
</gov:CQLQuery>
</dat:cqlQuery>
</dat:QueryRequest>
</soapenv:Body>
</soapenv:Envelope>
(The validity of the above SOAP request was verified using soapUI.)
In the attached ZIP file, the wstest.bat first sets the CLASSPATH environment variable to include the required JAR files, then executes the wstestX.groovy script (e.g. wstest1.groovy, wstest2.groovy, wstest3.groovy, wstest4.groovy) specified by the command line parameter.
Issue #1: "Error compiling schema from WSDL" ... "Two declarations cause a collision"
C:\groovywstest>.\wstest.bat 1
Caught: java.lang.reflect.UndeclaredThrowableException
...
Caused by: java.lang.RuntimeException: Error compiling schema from WSDL at {http
://cadsr-dataservice.nci.nih.gov/wsrf/services/cagrid/CaDSRDataService?wsdl}: Tw
o declarations cause a collision in the ObjectFactory class.
...
Caused by: com.sun.istack.SAXParseException2: Two declarations cause a collision
The above issue was caused by a problem mapping between XML and Java namespaces. To resolve this issue, I created a custom local copy of the WSDL files and annotated the <schema> elements with <jxb:package> bindings. In one case, a <jxb:typeName> XML tranform was also needed. I think it will be common for GroovyWS users to encounter this type of namespace mapping problem, so the GroovyWS documentation should include some information about how to customize JAXB bindings (see also http://java.sun.com/webservices/docs/1.5/tutorial/doc/JAXBUsing4.html).
Issue #2: No method to create an instance of an enumerated data type.
C:\groovywstest>.\wstest.bat 2
...
INFO: Created classes: gov.nih.nci.cagrid.metadata.dataservice.DomainModel,
...
Caught: java.lang.AssertionError: Expression: (EQUAL_TO != null). Values: EQUAL_TO = null
java.lang.AssertionError: Expression: (EQUAL_TO != null). Values: EQUAL_TO = null
...
This issue is also mentioned in GMOD-4 (see http://jira.codehaus.org/browse/GMOD-4), which includes a patch that adds a createEnum() method to the WSClient class. To work around this issue, I added a createEnum() method to the script.
Issue #3: Attempt to invoke the CaDSRDataService "query" web service operation returns null.
C:\groovywstest>.\wstest.bat 3
...
INFO: Created classes: gov.nih.nci.cagrid.metadata.dataservice.DomainModel,
...
Caught: java.lang.AssertionError: Expression: (result != null). Values: result =
null
java.lang.AssertionError: Expression: (result != null). Values: result = null
...
I think this is a severe defect in GroovyWS. Further testing with a soapUI MockService indicated that, although proxy.query(cqlQuery) returned a null result, it was not even issuing a request to the web service.
To troubleshoot further, I looked for GroovyWS source code and found something interesting in this file:
http://svn.codehaus.org/gmod/groovyws/trunk/src/main/java/groovyx/net/ws/AbstractCXFWSClient.java
An excerpt from that file indicates the AbstractCXFWSClient.invokeMethod method returns null if unable to get BindingOperationInfo for the method:
public Object invokeMethod(String name, Object args) {
Object[] objs = InvokerHelper.asArray(args);
try {
QName qname = new QName(getServiceNamespaceURI(), name);
BindingOperationInfo op = getBindingOperationInfo(qname);
if (op == null) {
return null;
}
...
I think this is dangerous, because it causes WSClient to fail silently when attempting to invoke a method or web service operation that doesn't exist
. Additionally, I didn't see any links to the (tiny ~25k) released groovyws-0.5.0.jar file, or any source code package for that release, on the GroovyWS web pages (http://groovy.codehaus.org/GroovyWS).
To get past this issue, I did some further investigation and discovered that proxy.getServiceNamespaceURI() returned an incorrect QName. As a workaround, I added code to the script, to get BindingOperationInfo using the correct QName, construct a QueryRequest, and invoke the web service operation.
So, the wstest4.groovy script is now able to successfully accomplish the goal:
C:\groovywstest>.\wstest.bat 4
...
INFO: Created classes: gov.nih.nci.cagrid.metadata.dataservice.DomainModel,
...
Bad QName: {http://dataservice.cadsr.nci.nih.gov/CaDSRDataService/service}query
null
Apr 28, 2009 6:09:32 PM groovyx.net.ws.AbstractCXFWSClient getBindingOperationInfo
WARNING: Using SOAP version: 1.1
Good QName: {http://dataservice.cadsr.nci.nih.gov/CaDSRDataService}query
[BindingOperationInfo: {http://dataservice.cadsr.nci.nih.gov/CaDSRDataService}query]
2450 - Post-Transplant Essential Data (TED)
2006 - Hematopoietic Stem Cell Transplant (HSCT) Infusion
2400 - Pre-Transplant Essential Data (TED)
2005 - Confirmation of HLA typing
2004 - Infectious Disease Markers
2900 - RECIPIENT DEATH DATA
2110 - Acute Myelogenous Leukemia Post-HSCT Data
2111 - Acute Lymphoblastic Leukemia Post-HSCT Data
I was unable to run these scripts under Eclipse (with Groovy 1.5.7 plugin), but I haven't yet investigated that problem any further (possible classloader issue?). Additionally, the time required to parse the WSDL and compile classes for the CaDSRDataService is quite noticeable, so it would be nice if there were a way to avoid recompiling each time the web service is accessed.
This experience has highlighted several issues with GroovyWS which I think should be considered:
1. Documentation should include information about how to set up local WSDL with customized JAXB bindings, to control Java <-> XML serialization.
2. The WSClient class should include a method, such as createEnum(), to create an instance of an enumerated type.
3. When unable to get BindingOperationInfo for a method, the AbstractCXFWSClient.invokeMethod method should throw an exception instead of returning null.
4. The AbstractCXFWSClient.getServiceNamespaceURI() method was returning an incorrect value; this should be fixed.
5. The huge groovyws-standalone-0.5.0.jar and groovyws-minimal-0.5.0.jar JAR files linked on the GroovyWS web site are not suitable for some environments. The web site should also include a link to the tiny groovyws-0.5.0.jar file (i.e.
http://repository.codehaus.org/org/codehaus/groovy/modules/groovyws/0.5.0/groovyws-0.5.0.jar).
6. The GroovyWS web site does not include a link to source code for the 0.5.0 release. Please add a link to the source code.
Aside, from the issues described here, I am very impressed with GroovyWS, and would like to use it in the future.
Thanks for this very exhaustive bug report - your work is impressive.
I have started to address your 6 issues. A full report and solution will come in few days - as I am finalizing the fixes.