вызвать веб-сервис JAX WS с сервера без http или сервлетов - PullRequest
3 голосов
/ 08 апреля 2011

Я создал сервер JAX-WS на основе SOAP (классы и WSDL созданы с wsgen). Я успешно проверил это, создав автономный сервер с Endpoint.publish () и успешно подключен к нему с клиенты Java, Perl и .NET. Теперь я хотел бы развернуть его на веб-фреймворк на основе не сервлетов (Play Framework).

к сожалению даже после просмотра всей сети для документов и кода, я не могу понять, как вызвать веб-сервис вне http изнутри сервера. С игровой стороны платформа направляет 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
    }
}

Ответы [ 3 ]

3 голосов
/ 19 апреля 2011

После множества проб и ошибок у меня есть решение. Ниже приведен класс invoker не сервлетов, а также пример его вызова.

Меня больше всего беспокоит то, что в моем решении много ссылок на 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>add the two jars jaxb-api.jar and jaxws-api.jar to the
 * jdk/jre/lib/endorsed directory
 *
 * <li>place all jars <i>except</i> the above two into the classpath
 *
 * </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);
    }
}
1 голос
/ 14 апреля 2011

Я не мог не спросить - почему вы смешиваете SOAP с Play?Все обоснование Play!иметь полностью RESTful, не сохраняющую состояния, не разделяющую архитектуру.Можете ли вы разработать конкретный вариант использования, который оправдывает такой дизайн?

0 голосов
/ 21 июля 2014

Вы можете сгенерировать Java Web Service Client с помощью инструмента wsimport для анализа опубликованного файла wsdl и создания необходимых клиентских файлов (заглушки) для доступа к опубликованному веб-сервису. Поскольку инструмент wsimport входит в комплект JDK и вы можете найти его в папке «JDK_PATH / bin», вам не нужно импортировать какую-либо библиотеку в свой проект.

wsimport -keep http://domain-name/ws/ws-name?wsdl

Класс, который аннотирует с использованием @WebServiceClient (например, Pgw_Service в моем случае), является вашим классом клиента веб-службы, и вы можете использовать его для вызова веб-службы:

    Pgw_Service service = new Pgw_Service();
    System.out.println(service.getPgwPort().test("saeed"));
...