title: Generic CXF WSDL-based Server using JAXB databinding
body: <p>I'm looking for a solution to integrate CXF into an application as a provider for Web services implementation. The application should be able to implement a web-service in a dynamic manner (this means that SEI classes are not available) based on the provided WSDL file. Since the application manages http requests and url mappings via its own servlet, its not feasible to use standard CXF servlet to publish endpoints. Also, I want to use JAXB databinding. Ideally CXF should call my <code>Object invoke(String oper, Object... args)</code> to do real processing of web service. Overall it should look like dynamic client but implemented for the server part.</p>
  
  <p>I've managed to get the code almost working, but bumped into a couple of things that I don't understand - about this later.</p>
  
  <p>First I read WSDL into a String and create its definition. Definition is precached in wsdlManager to be accessible via unique reference. Than I create JaxWS dynamic client and grab JAXB databinding that was generated for it by CXF.</p>
  
  <pre><code>        WSDLManager wsdlManager = serviceBus.getExtension(WSDLManager.class);
          WSDLFactory wsdlFactory = wsdlManager.getWSDLFactory();
  
          // Reader
          WSDLReader reader = wsdlFactory.newWSDLReader();
          reader.setFeature("javax.wsdl.verbose", true);
          reader.setFeature("javax.wsdl.importDocuments", true);
  
          Definition def = reader.readWSDL(null, TypeCast.getInputSource(wsdl)); // wsdl is a String containing wsdl definition
  
          // Precache definition using listenerRef as unique identifier
          wsdlManager.addDefinition(listenerRef, def);
  
          String[] compileOptions = null;
  
          // Create JAXWS dynamic client using precached address
          Client client = createClient(listenerRef, simpleDataBinding, allowElementReferences, compileOptions);
  
          if (Logger.isDebugEnabled()) 
             Logger.debug("WebServiceProcessor.initServiceListener: service client is created succefully: " + listenerName);
  
          EndpointInfo cei = client.getEndpoint().getEndpointInfo();
  
          // Use JAXB generated databinding
          DataBinding db = client.getEndpoint().getService().getDataBinding();
  
          if (Logger.isDebugEnabled()) 
             Logger.debug("WebServiceProcessor.initServiceListener: databinding: " + db);
  
  </code></pre>
  
  <p>The creation of dynamic client is straightforward</p>
  
  <pre><code>   public Client createClient(String serviceDescription, boolean simpleDataBinding, boolean allowElementReferences, String[] schemaOptions) {
         ClassLoader old = Thread.currentThread().getContextClassLoader();
  
         try {
            ParentClassLoader dynaLoader = new ParentClassLoader();
  
            JaxWsDynamicClientFactory dynamicClientFactory = JaxWsDynamicClientFactory.newInstance(serviceBus);
            dynamicClientFactory.setSimpleBindingEnabled(simpleDataBinding);
            dynamicClientFactory.setAllowElementReferences(allowElementReferences);
  
            if (schemaOptions != null) {
               dynamicClientFactory.setSchemaCompilerOptions(schemaOptions);
            }
  
            return dynamicClientFactory.createClient(serviceDescription, dynaLoader);
  
         } catch (Throwable ex) {
            Logger.error("WebServiceProcessor.createClient: exception is caught: " + ex, ex);
            return null;
  
         } finally {
            Thread.currentThread().setContextClassLoader(old);
         }
     }
  </code></pre>
  
  <p>Next, to create the server stuff some helper classes are declared</p>
  
  <pre><code>   protected class MyWSDLServiceFactory extends WSDLServiceFactory {
  
         public MyWSDLServiceFactory(Bus b, Definition d) {
            super(b, d);
         }
  
         @Override
         public Service create() {
            Service svc = super.create();
  
            // Post init
            initializeDefaultInterceptors();
            initializeDataBindings(); 
  
            return svc;
         }
     }
  
     public class MyInvoker extends AbstractInvoker {
  
       protected final Object implementor = new Object();
  
       public MyInvoker() {
       }
  
       @Override
       public Object getServiceObject(Exchange context) {
          return implementor;
       }
  
       protected void throwable() throws Exception {
  
       }
  
       @Override
       public Object invoke(Exchange exchange, Object o) {
  
          List&lt;Object&gt; params = null;
          if (o instanceof List) {
              params = CastUtils.cast((List&lt;?&gt;)o);
          } else if (o != null) {
              params = new MessageContentsList(o);
          }
  
          if (Logger.isTraceEnabled()) {
             for (Object arg : params)
                Logger.trace("MyInvoker.invoke: arg: " + arg);
          }
  
          // Method holding declararions of throwable exceptions 
          Method m = null;
          try {
             m = MsyInvoker.class.getMethod("throwable");
          } catch (NoSuchMethodException ex) {
             // Strange
          }
  
          return invoke(exchange, null, m, params);
       }
  
       @Override
       protected Object performInvocation(Exchange exchange, Object serviceObject, Method m, Object[] paramArray) throws Exception {
          Message inMessage = exchange.getInMessage();
          BindingOperationInfo bop = exchange.getBindingOperationInfo();
  
          String oper = bop.getName().getLocalPart();
  
          // Process request
          return processWebListenerRequest(oper, paramArray);
       }
     }
  
     protected class MyDestinationFactory implements DestinationFactory {
  
       protected final Set&lt;String&gt; prefixes = Collections.unmodifiableSet(new HashSet&lt;String&gt; (Arrays.asList("http://", "https://")));
  
       @Override
       public Destination getDestination(EndpointInfo ei, Bus bus) throws IOException {
           return new MyDestination(ei, ei.getAddress());
       }
  
       @Override
       public Set&lt;String&gt; getUriPrefixes() {
           return prefixes;
       }
  
       @Override
       public List&lt;String&gt; getTransportIds() {
           return null;
       }
     }
  
     protected class MyDestination extends ServletDestination {
  
       public MyDestination(EndpointInfo ei, String path) throws IOException {
          super(serviceBus, null, ei, path, false);
  
          // Disable async support
          isServlet3 = false;
       }
  
       @Override
       protected void setupMessage(final Message inMessage, final ServletConfig config, final ServletContext context, final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
          super.setupMessage(inMessage, config, context, req, resp);
       }
  
       @Override
       protected String getBasePath(String contextPath) throws IOException {
           if (endpointInfo.getAddress() == null) {
               return "";
           }
           return endpointInfo.getAddress();
       }
     }
  
  </code></pre>
  
  <p>Then we are ready to create the server:</p>
  
  <pre><code>        MyWSDLServiceFactory sf = new MyWSDLServiceFactory(serviceBus, def);
          sf.setAllowElementRefs(allowElementReferences);
          sf.setDataBinding(db);
  
          Service svc = sf.create();
  
          // Clear cached definition
          wsdlManager.removeDefinition(def);
  
          svc.setInvoker(new MyInvoker());
  
          // Create endpoints
  
          for (ServiceInfo inf : svc.getServiceInfos()) {
             for (EndpointInfo ei : inf.getEndpoints()) {
                if (ei.getName().equals(cei.getName())) {
  
                   if (Logger.isDebugEnabled()) 
                      Logger.debug("WebServiceProcessor.initServiceListener: endpoint: " + ei.getName());
  
                   String addr = "/" + listenerRef;
  
                   try {
                       ei.setAddress(addr);
                       JaxWsEndpointImpl ep = new JaxWsEndpointImpl(serviceBus, svc, ei);
  
                       svc.getEndpoints().put(ei.getName(), ep);
  
                       ep.addHandlerInterceptors();
                       ep.getInInterceptors().add(new SoapUtil.SoapInLogger());
  
                       BindingFactoryManager bfm = serviceBus.getExtension(BindingFactoryManager.class);
  
                       // tried this but no effect
                       // ei.getBinding().setProperty("soap.force.doclit.bare", Boolean.TRUE);
  
                       String bindingId = ei.getBinding().getBindingId();
  
                       if (Logger.isDebugEnabled()) 
                          Logger.debug("WebServiceProcessor.initServiceListener: binding id: " + bindingId);
  
                       BindingFactory bindingFactory = bfm.getBindingFactory(bindingId);
  
                       Server server = new ServerImpl(serviceBus, ep, new MyDestinationFactory(), bindingFactory);
  
                       if (Logger.isDebugEnabled()) 
                          Logger.debug("WebServiceProcessor.initServiceListener: starting server: " + ei.getName());
  
                       server.start();
  
                       if (Logger.isDebugEnabled()) 
                          Logger.debug("WebServiceProcessor.initServiceListener: server is started: " + server.isStarted());
  
                       // Set reference
                       listeners.put(listenerRef, server); // Our map to keep web server listeners
                   } catch (EndpointException e) {
                       throw new ServiceConstructionException(e);
                   }
                }
             }
          }
  </code></pre>
  
  <p>The server invocation is looks like</p>
  
  <pre><code>     String address = "/" + listenerRef;
       Server server = listeners.get(listenerRef); // Find our server listener in a map
  
       if (server != null) {
          Endpoint ep = server.getEndpoint();
          EndpointInfo ei = ep.getEndpointInfo();
  
          if (Logger.isDebugEnabled())
            Logger.debug("WebServiceProcessor.invoke: endpoint: " + listenerName);
  
          try {
             AbstractHTTPDestination dest = (AbstractHTTPDestination) server.getDestination();
             AsyncContext asyncCtx = requestContext.getAsyncContext();
  
             HttpServletRequest req = (HttpServletRequest) asyncCtx.getRequest();
             HttpServletResponse resp = (HttpServletResponse) asyncCtx.getResponse();
             ServletContext sctx = req.getServletContext();
             ServletConfig scfg = null; 
  
             if (Logger.isDebugEnabled())
               Logger.debug("WebServiceProcessor.invoke: destination resolved successfully: " + listenerName);
  
             // Trigger CXF processing
             dest.invoke(scfg, sctx, req, resp);
  
             if (Logger.isDebugEnabled())
               Logger.debug("WebServiceProcessor.invoke: endpoint processed successfully: " + listenerName);
  
          } catch (Exception ex) {
             Logger.error("WebServiceProcessor.invoke: exception is caught: " + ex, ex);
          }
       }
  </code></pre>
  
  <p>As I already mentioned, the solution almost works, I tried to test it with CXF 3.3 and one WSDL I took as example <a href="http://www.dneonline.com/calculator.asmx?WSDL" rel="nofollow noreferrer">http://www.dneonline.com/calculator.asmx?WSDL</a>. I managed to call the service with SoapUI and get the response. But now is the strange part. When I call the webservice with standard request</p>
  
  <pre><code>&lt;soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"&gt;
     &lt;soap:Header/&gt;
     &lt;soap:Body&gt;
     &lt;Add xmlns="http://tempuri.org/"&gt;
         &lt;intA&gt;1&lt;/intA&gt;&lt;intB&gt;2&lt;/intB&gt; 
     &lt;/Add&gt;
     &lt;/soap:Body&gt;
  &lt;/soap:Envelope&gt;
  </code></pre>
  
  <p>It fails with Unmarshalling Error </p>
  
  <pre><code>org.apache.cxf.interceptor.Fault: Unmarshalling Error: unexpected element (uri:"http://tempuri.org/", local:"intA"). Expected elements are &lt;{http://tempuri.org/}Add&gt;,&lt;{http://tempuri.org/}AddResponse&gt;,&lt;{http://tempuri.org/}Divide&gt;,&lt;{http://tempuri.org/}DivideResponse&gt;,&lt;{http://tempuri.org/}Multiply&gt;,&lt;{http://tempuri.org/}MultiplyResponse&gt;,&lt;{http://tempuri.org/}Subtract&gt;,&lt;{http://tempuri.org/}SubtractResponse&gt; 
      at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:932) ~[cxf-rt-databinding-jaxb-3.3.6.jar:3.3.6]
      at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:738) ~[cxf-rt-databinding-jaxb-3.3.6.jar:3.3.6]
      at org.apache.cxf.jaxb.io.DataReaderImpl.read(DataReaderImpl.java:170) ~[cxf-rt-databinding-jaxb-3.3.6.jar:3.3.6]
      at org.apache.cxf.wsdl.interceptors.DocLiteralInInterceptor.getPara(DocLiteralInInterceptor.java:325) ~[cxf-rt-wsdl-3.3.6.jar:3.3.6]
      at org.apache.cxf.wsdl.interceptors.DocLiteralInInterceptor.handleMessage(DocLiteralInInterceptor.java:127) ~[cxf-rt-wsdl-3.3.6.jar:3.3.6]
  </code></pre>
  
  <p>But it successfully passes validation with</p>
  
  <pre><code>&lt;soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"&gt;
     &lt;soap:Header/&gt;
     &lt;soap:Body&gt;
     &lt;Add xmlns="http://tempuri.org/"&gt;
         &lt;Add&gt;
         &lt;intA&gt;1&lt;/intA&gt;&lt;intB&gt;2&lt;/intB&gt; 
         &lt;/Add&gt;
     &lt;/Add&gt;
     &lt;/soap:Body&gt;
  &lt;/soap:Envelope&gt;
  </code></pre>
  
  <p>But in this case the parameters that are passed to MyInvoker are an array of two null elements. Nevertheless it generates the correctly formatted (except that the calculated value is wrong because input parameters are null) response</p>
  
  <pre><code>&lt;soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"&gt;
     &lt;soap:Body&gt;
        &lt;ns1:AddResponse xmlns:ns1="http://tempuri.org/"&gt;
           &lt;AddResult xmlns="http://tempuri.org/"&gt;0&lt;/AddResult&gt;
        &lt;/ns1:AddResponse&gt;
     &lt;/soap:Body&gt;
  &lt;/soap:Envelope&gt;
  </code></pre>
  
  <p>So the questions are - what could go wrong with unmarshalling of the following valid request?</p>
  
  <pre><code>&lt;soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"&gt;
     &lt;soap:Header/&gt;
     &lt;soap:Body&gt;
     &lt;Add xmlns="http://tempuri.org/"&gt;
         &lt;intA&gt;1&lt;/intA&gt;&lt;intB&gt;2&lt;/intB&gt; 
     &lt;/Add&gt;
     &lt;/soap:Body&gt;
  &lt;/soap:Envelope&gt;
  </code></pre>
  
  <p>I have checked the CXF dynamic client invocation with the tested WSDL, from which I borrowed JAXB databinding, and it generates the exactly the same request when calling this service, but seems not to be able to unmarshall it for some reason. </p>
  
  <p>Another question, I think it relates to the first one, is why the unmarshalled parameters in are nulls in case of second request? Any suggestion to where to look next?</p>
  
  <p>Thanks in advance</p>