Erroneous support of Jaxb web services taking serveral Holder arguments 
------------------------------------------------------------------------

                 Key: CAMEL-5112
                 URL: https://issues.apache.org/jira/browse/CAMEL-5112
             Project: Camel
          Issue Type: Bug
          Components: camel-soap
    Affects Versions: 2.9.0
         Environment: $> java -version
java version "1.6.0_29"
Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-11D50d)
Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02-402, mixed mode)

$> uname -a
11.3.0 Darwin Kernel Version 11.3.0: Thu Jan 12 18:47:41 PST 2012; 
root:xnu-1699.24.23~1/RELEASE_X86_64 x86_64

            Reporter: Florent Legendre


Hi,
*NB*
I ran into a problem while using the camel-soap module. I have created a 
patched version of the problematic classes and am totally open for submitting 
them for review in hope that it could increase the chance for this bug to be 
fixed in a near future. 

*The problem*
I have to integrate with a web service with that type of signature (generated 
from the original wsdl file with the help of wsimport):

{{code}}
@WebMethod
@WebResult(name = "Details", targetNamespace = 
"http://example.com/service.xsd";, partName = "response")
public Details fetchDetails(
        @WebParam(name = "fetchDetails", targetNamespace = 
"http://example.com/service.xsd";, partName = "fetchDetails")
        Personsearch fetchDetails,
        @WebParam(name = "Session", targetNamespace = 
"http://example.com/service.xsd";, header = true, mode = WebParam.Mode.INOUT, 
partName = "Session")
        Holder<Session> session,
        @WebParam(name = "Transaction", targetNamespace = 
"http://example.com/service.xsd";, header = true, partName = "Transaction")
        Transaction transaction,
        @WebParam(name = "Transactioninfo", targetNamespace = 
"http://example.com/service.xsd";, header = true, mode = WebParam.Mode.OUT, 
partName = "Transactioninfo")
        Holder<Transactioninfo> transactionInfo)
        throws FetchDetailsException
    ;
}}
{{code}}

The call to the web service is set up as documented in the wiki (all this done 
in the context of a {{Processor#process(Exchange)}} method)
* Create a bean invocation
* Set the Method object representing the method to be called (fetchDetails in 
my case) on the bean invocation
* build the parameters for the method call and set them on the bean invocation.
* Set the bean invocation in the out body of the exchange

This kind of setup leads to incorrect generation of the xml sent to the 
webservice. The reason for this is:
* When specifying the method to invoke i have to write 
{{beanInvocation.setMethod(ServicePortType.class.getMethod(WS_METHOD_NAME, 
Personsearch.class, Holder.class, Transaction.class, Holder.class));}}. Note 
that this method signature contains two Holder objects.
* When {{ServiceInterfaceStrategy}} goes through {{#analyzeServiceInterface}} 
it uses the name of the class object passed in the signature as key in the 
{{ServiceInterfaceStrategy.inTypeNameToQName}} Map. 
* Because there are two {{Holder}} instances in the signature it means that 
only one of the two QName which are added to the map will be found, as they 
both rely on the same {{Holder.class}}-based key
* The problem above occurs when 
{{ServiceInterfaceStrategy#findQNameForSoapActionOrObject}} reads from 
{{ServiceInterfaceStrategy.inTypeNameToQName}}, getting the exact same QName 
out of the map for both objects, hence generating erroneous xml (Note that 
nothing fails, it just sends rubbish to the webservice)

*A solution*
Files that I "Patched" to get this case to work:
* org.apache.camel.dataformat.soap.name.ElementNameStrategy
* org.apache.camel.dataformat.soap.name.ServiceInterfaceStrategy
* org.apache.camel.dataformat.soap.name.TypeNameStrategy
* org.apache.camel.dataformat.soap.SoapJaxbDataFormat


My solution relies on the following:
* For the end user the "receipe" is still exactly the same with use of 
BeanInvocation
* I have created a utility class to generate suitable keys for 
adding/retrieving Holder objects (see further down).
* I modified the method 
{{PatchedElementNameStrategy#findQNameForSoapActionOrObject(String soapAction, 
Object object)}} to take an object parameter instead of a class. This gives 
this method the correct tool (the object, not the class) to produce a key which 
actually retrieves the correct QName

All this works fine for me and I would like to submit my patch to the project.
{{code}}
package org.apache.camel.dataformat.soap.name;

import javax.xml.ws.Holder;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public final class ElementNameStrategyUtils {

    private ElementNameStrategyUtils() {
        // utility class
    }

    /**
     * Generates a suitable key for adding a QName in one of the 
<code>Map<String, QName></code> <code>PatchedServiceInterfaceStrategy</code>.
     *
     * @param type
     * @return
     */
    public static String getTypeNameForType(Type type) {
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            StringBuffer typeName = new StringBuffer();
            Class<?> rawTypeAsClass = (Class<?>) parameterizedType.getRawType();
            typeName.append(rawTypeAsClass.getName());
            for (int i = 0; i < 
parameterizedType.getActualTypeArguments().length; i++) {
                Type actualTypeArg = 
parameterizedType.getActualTypeArguments()[i];
                typeName.append("-");
                typeName.append(getTypeNameForType(actualTypeArg));
            }
            return typeName.toString();
        } else {
            Class<?> typeAsClass = (Class<?>) type;
            return typeAsClass.getName();
        }
    }

    /**
     * Generates a suitable key for retrieving a QName from one of the 
<code>Map<String, QName></code> <code>PatchedServiceInterfaceStrategy</code>.
     *
     * @param object
     * @return
     */
    public static String getTypeNameForObject(Object object) {
        if (object instanceof Holder) {
            Holder holder = (Holder) object;
            StringBuffer typeName = new StringBuffer();
            typeName.append(holder.getClass().getName());
            if (holder.value != null) {
                typeName.append("-");
                typeName.append(getTypeNameForObject(holder.value));
            }
            return typeName.toString();
        } else {
            return object.getClass().getName();
        }
    }


}
{{code}}

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: 
https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

Reply via email to