Uploaded image for project: 'Camel'
  1. Camel
  2. CAMEL-5112

camel-soap - Erroneous support of Jaxb web services taking several Holder arguments

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Minor
    • Resolution: Abandoned
    • 2.9.0
    • Future
    • camel-soap
    • None
    • Unknown

    Description

      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):

      @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
          ;
      }}
      

      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.

      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();
              }
          }
      
      
      }
      

      Attachments

        Activity

          People

            davsclaus Claus Ibsen
            renflo Florent Legendre
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: