jax-ws - 从服务器调用 JAX WS Web 服务,无需 http 或 servlet

标签 jax-ws playframework

我创建了一个基于 SOAP 的 JAX-WS 服务器(使用以下命令创建的类和 WSDL) WSGEN)。我已经通过创建一个独立的成功测试了这一点 使用 Endpoint.publish() 服务器并成功连接到它 java、perl 和 .NET 客户端。现在我想将它部署在 基于非 servlet 的 Web 框架(Play 框架)。

不幸的是,即使 在网上查找文档和代码后我无法弄清楚 如何从服务器内部调用 http 之外的 Web 服务。 在游戏方面,框架将 http POST 请求路由到 静态java方法。很容易就能拿到肥皂包,但我却拿不到 弄清楚如何将其传递给 JAX WS 进行调用。

简而言之,这就是我想做的事情

public class Application extends Controller 
{
    // function referenced by routing table
    public static void func1() {
        // TODO: use play to get SOAP request from caller
        String soapRequest = playFrameworkCode();

        Object implementor = MyJaxWsWebService();

        // !!! insert JAX WS code here !!!
        Object magicJaxWsObject;
        String soapResult = magicJaxWsObject.invoke(soapRequest);

        // TODO: use Play to return SOAP result to caller
    }
}

最佳答案

经过多次尝试和错误,我找到了解决方案。下面是非 servlet 调用程序类以及调用它的示例。

我最担心的是我的解决方案有很多对 com.sun.xml.ws 的引用。*我这样做是不是搬起石头砸自己的脚?是否有任何 JAX WS 开发人员阅读本文后可以对这个解决方案表示赞同或反对?


package controllers.ws;

import com.sun.xml.ws.api.BindingID;
import com.sun.xml.ws.api.WSBinding;
import com.sun.xml.ws.api.message.Packet;
import com.sun.xml.ws.api.server.InstanceResolver;
import com.sun.xml.ws.api.server.Invoker;
import com.sun.xml.ws.api.server.SDDocumentSource;
import com.sun.xml.ws.api.server.WSEndpoint;
import com.sun.xml.ws.binding.BindingImpl;
import com.sun.xml.ws.message.saaj.SAAJMessage;
import com.sun.xml.ws.server.EndpointFactory;
import com.sun.xml.ws.transport.Headers;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeader;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.handler.MessageContext;

/**
 * This is the glue between a non-servlet based web server and the JAX
 * WS server functionality.  Given a web service definition (WSDL
 * file, generated classes) offer a function that takes unmarshalled
 * soap, runs the service, and returns the soap response.
 *
 * This currently does not run on jax-ws 2.0.x (the version installed
 * in most jdk6 installs) but it does work with a custom download of
 * jax-ws.  You must do the following to get this working:
 *
 * <ul>
 *
 * <li>download the zipfile from http://jax-ws.java.net/ This class
 * has been tested to work with v2.2.3
 *
 * </li><li>add the two jars jaxb-api.jar and jaxws-api.jar to the
 * jdk/jre/lib/endorsed directory
 *
 * </li><li>place all jars <i>except</i> the above two into the classpath
 *
 * </li></ul>
 *
 * Note: this code is only used on the server side so it is something
 * we should always have a lot of control over.  The client can still
 * connect with any version of jax ws or any soap/wsdl framework.
 *
 * TODO: it's not clear how stable this code is.  Writing it required
 * examining the source of the entire jax ws framework and performing
 * lots of trial and error.  We should really check with the jax ws
 * guys for guidance.
 */
public class ServiceInvoker {

    private ExecutorService executorService;
    private WSEndpoint endpoint;
    private Class clazz;

    public ServiceInvoker(Class clazz) {
        // interweb wisdom says that generic class info is compile
        // time only so the only way to get the class of T
        // (e.g. T.getClass()) is to have an instance of T or
        // explicitly pass the class in like this.
        this.clazz = clazz;
    }

    /**
     * Starts the service including creating an executor to run the commands
     *
     * @param service the QName of the service as specified in the WSDL
     * @param port the QName of the port as specified in the WSDL
     * @param wsdlFname the actual WSDL file assumed to be in the root
     * of the class dir
     */
    public void start(QName service, QName port, URL wsdlUrl) {
        Invoker invoker = InstanceResolver.createDefault(clazz).createInvoker();
        WSBinding binding = BindingImpl.create(BindingID.parse(clazz));
        SDDocumentSource doc = SDDocumentSource.create(wsdlUrl);

        endpoint = 
            EndpointFactory.createEndpoint(
                                           clazz, // Class implType, 
                                           true, // boolean processHandlerAnnotation, 
                                           invoker, // @Nullable Invoker invoker,
                                           service, // @Nullable QName serviceName, 
                                           port, // @Nullable QName portName,
                                           null, // @Nullable Container container, 
                                           binding, // @Nullable WSBinding binding,
                                           doc, // @Nullable SDDocumentSource primaryWsdl,
                                           null, //@Nullable Collection metadata,
                                           null, // EntityResolver resolver,
                                           true // boolean isTransportSynchronous
                                           );

        executorService = Executors.newCachedThreadPool();
        endpoint.setExecutor(executorService);
    }

    /**
     * Cleans up the invoker by shutting down any remaining threads.
     * The JVM may not terminate if this function is not called.
     */
    public void stop() {
        if (executorService != null) {
            executorService.shutdown();
        }
    }

    /**
     * Given a WSDL compliant soap request for the service, runs the
     * service, and returns a WSDL compliant SOAP response/fault.
     */
    public String invoke(String soapText) {

        SOAPMessage sm = stringToSoap(soapText);
        Packet packet = createPacket(sm);

        MyOnCompletion c = new MyOnCompletion();
        endpoint.schedule(packet,c);
        return c.waitForResult();
    }

    private static SOAPMessage stringToSoap(String soapText) {
        SOAPMessage message;
        try {
            // Create SoapMessage
            MessageFactory msgFactory = MessageFactory.newInstance();
            message = msgFactory.createMessage();
            SOAPPart soapPart = message.getSOAPPart();

            // Load the SOAP text into a stream source
            byte[] buffer = soapText.getBytes();
            ByteArrayInputStream stream = new ByteArrayInputStream(buffer);
            StreamSource source = new StreamSource(stream);

            // Set contents of message
            soapPart.setContent(source);

            return message;
        } catch (SOAPException  e) {
            // TODO: what do we return when we can't parse the
            // incoming soap?
            System.out.println("SOAPException : " + e);
            return null;
        }
    }

    private static Packet createPacket(SOAPMessage arg) {
        Iterator iter = arg.getMimeHeaders().getAllHeaders();
        Headers ch = new Headers();
        while(iter.hasNext()) {
            MimeHeader mh = (MimeHeader) iter.next();
            ch.add(mh.getName(), mh.getValue());
        }
        Packet packet = new Packet(new SAAJMessage(arg));
        packet.invocationProperties.put(MessageContext.HTTP_REQUEST_HEADERS, ch);
        return packet;
    }

    /**
     * The JAX WS invoker framework is designed for asynchronous
     * calls.  We want to treat the calls synchronously so this class
     * allows us to easily wait for the invoked call to complete and
     * return the value to the caller.
     */
    private static class MyOnCompletion implements WSEndpoint.CompletionCallback {
        private Object mutex;
        private String result;
        public MyOnCompletion() {
            // we could use the MyOnCompletion class as a mutex but
            // this is safer (no one else can send notify messages)
            mutex = new Object();
        }
        public void onCompletion(Packet response) {
            try {
                SOAPMessage sm = response.getMessage().readAsSOAPMessage();
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                sm.writeTo(out);
                synchronized(mutex) {
                    result = out.toString();
                    mutex.notify();
                }
            } catch (SOAPException e) {
                // we should never expect the JAX WS framework to
                // return invalid SOAP
                throw new RuntimeException(e);
            } catch (IOException e) {
                // we should never expect the JAX WS framework to
                // return invalid SOAP
                throw new RuntimeException(e);
            }
        }
        public String waitForResult() {
            synchronized(mutex) {
                if (result != null) {
                    return result;
                }
                try {
                    mutex.wait();
                } catch (InterruptedException e) {
                }
                return result;
            }
        }
    }
}

调用者...


public class Application extends Controller 
{
   /**
     * The entrypoint to a webservice call
     *
     * Strangely the argument "String body" hs to be named "body" as
     * it triggers Play! to pass the raw html POST info.
     */
    public static void fibonacci(String body) {

        // TODO: it would probably be good to cache this and reuse it
        // over many calls. (not sure how expensive this is)
        ServiceInvoker invoker;
        {
            invoker = new ServiceInvoker(Fibonacci.class);

            QName service = 
                new QName(
                          "http://scharp.org/fib/", 
                          "FibonacciService");
            QName port = 
                new QName(
                          "http://scharp.org/fib/", 
                          "FibonacciPort");

            URL wsdlUrl;
            try {
                wsdlUrl = Play.getFile("lib/FibonacciService.wsdl").toURI().toURL();
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            }

            invoker.start(service, port, wsdlUrl);
        }

        String requestOrFault = invoker.invoke(body);

        invoker.stop();

        renderXml(requestOrFault);
    }
}

关于jax-ws - 从服务器调用 JAX WS Web 服务,无需 http 或 servlet,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5597935/

相关文章:

java - 从 JAX-WS 生成的 WSDL 中隐藏枚举元素

java - xsd :datetime and XmlGregorianCalendar causes NullPointerException

hibernate - 我将如何使用 JPA 在同一对象上映射父/子关系

java - 公开一个以对象作为参数的方法的 Web 服务

java - 在 JAX-WS 中获取 Web 服务上下文监听器?

java - IntelliJ Idea 无法解析 Scala 管理源中的符号 (sbt-buildinfo)

playframework - 用于微服务应用的 Docker

scala - 如何检查字段构造函数中的元素是否为 null 或为空?

scala - Websocket 'hard' 断开连接不明显?

java - 在 eclipse 中调试 jax-ws 客户端