使用JAXB数据绑定的基于CXF的通用WSDL服务器

问题描述 投票:0回答:1

我正在寻找一种解决方案,以将CXF集成到作为Web服务实现提供者的应用程序中。应用程序应该能够基于提供的WSDL文件以动态方式(这意味着SEI类不可用)来实现Web服务。由于应用程序通过其自己的servlet管理http请求和url映射,因此使用标准CXF servlet发布端点是不可行的。另外,我想使用JAXB数据绑定。理想情况下,CXF应该调用我的Object invoke(String oper, Object... args)来真正处理Web服务。总体来说,它应该看起来像动态客户端,但是是为服务器部分实现的。

我设法使代码几乎可以正常工作,但是碰到了一些我不理解的事情-稍后再说。

首先,我将WSDL读入字符串并创建其定义。定义已预先缓存在wsdlManager中,可以通过唯一引用进行访问。比起创建JaxWS动态客户端,并获取CXF为其生成的JAXB数据绑定。

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

动态客户端的创建很简单

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

接下来,要创建服务器内容,需要声明一些帮助程序类

   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<Object> params = null;
        if (o instanceof List) {
            params = CastUtils.cast((List<?>)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<String> prefixes = Collections.unmodifiableSet(new HashSet<String> (Arrays.asList("http://", "https://")));

     @Override
     public Destination getDestination(EndpointInfo ei, Bus bus) throws IOException {
         return new MyDestination(ei, ei.getAddress());
     }

     @Override
     public Set<String> getUriPrefixes() {
         return prefixes;
     }

     @Override
     public List<String> 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();
     }
   }

然后我们准备创建服务器:

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

服务器调用看起来像

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

正如我已经提到的,该解决方案几乎可以用,我尝试使用CXF 3.3和一个以http://www.dneonline.com/calculator.asmx?WSDL为例的WSDL对其进行测试。我设法通过SoapUI调用了该服务并获得了响应。但是现在是奇怪的部分。当我通过标准请求调用网络服务时

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
   <soap:Header/>
   <soap:Body>
   <Add xmlns="http://tempuri.org/">
       <intA>1</intA><intB>2</intB> 
   </Add>
   </soap:Body>
</soap:Envelope>

失败,出现解组错误

org.apache.cxf.interceptor.Fault: Unmarshalling Error: unexpected element (uri:"http://tempuri.org/", local:"intA"). Expected elements are <{http://tempuri.org/}Add>,<{http://tempuri.org/}AddResponse>,<{http://tempuri.org/}Divide>,<{http://tempuri.org/}DivideResponse>,<{http://tempuri.org/}Multiply>,<{http://tempuri.org/}MultiplyResponse>,<{http://tempuri.org/}Subtract>,<{http://tempuri.org/}SubtractResponse> 
    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]

但是它成功通过了验证

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
   <soap:Header/>
   <soap:Body>
   <Add xmlns="http://tempuri.org/">
       <Add>
       <intA>1</intA><intB>2</intB> 
       </Add>
   </Add>
   </soap:Body>
</soap:Envelope>

但是在这种情况下,传递给MyInvoker的参数是两个空元素的数组。不过,它会生成格式正确的(除了由于输入参数为空而导致计算值错误之外)响应

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
   <soap:Body>
      <ns1:AddResponse xmlns:ns1="http://tempuri.org/">
         <AddResult xmlns="http://tempuri.org/">0</AddResult>
      </ns1:AddResponse>
   </soap:Body>
</soap:Envelope>

所以问题是-取消整理以下有效请求可能会出什么问题?

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
   <soap:Header/>
   <soap:Body>
   <Add xmlns="http://tempuri.org/">
       <intA>1</intA><intB>2</intB> 
   </Add>
   </soap:Body>
</soap:Envelope>

我已经用经过测试的WSDL检查了CXF动态客户端调用,我从中借用了JAXB数据绑定,并且在调用此服务时它会生成完全相同的请求,但由于某种原因似乎无法将其解组。

另一个问题,我认为它与第一个有关,为什么在第二个请求的情况下,为什么未编组的参数为null?对下一步去哪里有任何建议?

提前感谢

java wsdl cxf
1个回答
0
投票

这里是我自己的问题的答案。如果通过从动态客户端重用CXF服务实例并添加一些服务器部分拦截器,事情将变得正确:

        Service svc = client.getEndpoint().getService();

        // Server part interceptors
        svc.getInInterceptors().add(new ServiceInvokerInterceptor());
        svc.getInInterceptors().add(new OutgoingChainInterceptor());
        svc.getInInterceptors().add(new OneWayProcessorInterceptor());

© www.soinside.com 2019 - 2024. All rights reserved.