Создайте клиент веб-службы Soap, используя документы wsdl в Eclipse

Мне нужно создать клиент веб-службы на Java с помощью Eclipse, который использует onvif wsdl.

Я провел несколько часов, не найдя, как это сделать, это первый раз, когда я использую мыло, мой опыт был в REST.

Я испробовал много руководств, таких как это, чтобы создать клиент веб-службы, но когда я пытаюсь выбрать файл wsdl с моего локального диска, eclipse показывает ошибку Could not retrieve the WSDL file ..., структура ссылок, которую я использовал для файла, была file:/C:/ONVIF/media.wsdl.

Мне нужно использовать любую среду Java, поддерживающую WS-Notification, для реализации моего клиента.

Не могли бы вы рассказать мне, как реализовать клиентскую веб-службу, которая использует файлы WSDL.
Нужен ли мне веб-сервер для реализации клиента веб-службы мыла?
Если да, то почему?


person Mike Albren    schedule 10.01.2012    source источник
comment
Я считаю, что JAX-WS будет поддерживать это (поддержка клиента предоставляется как часть API Java 6).   -  person McDowell    schedule 10.01.2012


Ответы (4)


Вот полный код и руководство о том, как использовать один из файлов wsdl ONVIF (devicemgmt.wsdl) и как использовать его для подключения к устройству:

package test;

import java.io.IOException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

import org.onvif.ver10.device.wsdl.Device;
import org.onvif.ver10.schema.DateTime;
import org.onvif.ver10.schema.SystemDateTime;
import org.onvif.ver10.schema.Time;

import com.sun.org.apache.xml.internal.security.utils.Base64;

public class OnvifTest {

    private static TimeZone utc = TimeZone.getTimeZone("UTC");
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

    static {
        sdf.setTimeZone(utc);
    }

    private static long serverTime = 0;
    private static long clientTime = 0;


    private static final String ip = "...";
    private static final String user = "...";
    private static final String pass = "...";
    // Some cameras (e.g. Axis) require that you set the user/pass on the ONVIF section in it's web interface
    // If the camera is reset to factory defaults and was never accessed from the web, then
    // either no user/pass is needed or the default user/pass can be used

    @SuppressWarnings("rawtypes")
    public static void main(String[] args) throws IOException {

        // The altered wsdl file
        URL url = new URL("file://"+System.getProperty("user.home")+"/onvif/devicemgmt.wsdl");
        // This file was downloaded from the onvif website and added a mock service in order to make it complete:
        //  <wsdl:service name="DeviceService">  
        //      <wsdl:port name="DevicePort" binding="tds:DeviceBinding">  
        //          <soap:address location="http://localhost/onvif/device_service"/>  
        //      </wsdl:port>  
        //  </wsdl:service>
        // The altered file was then used to generate java classes using $JAVA_HOME/bin/wsimport -Xnocompile -extension devicemgmt.wsdl 

        QName qname = new QName("http://www.onvif.org/ver10/device/wsdl", "DeviceService");
        Service service = Service.create(url, qname);
        Device device = service.getPort(Device.class);

        BindingProvider bindingProvider = (BindingProvider)device;

        // Add a security handler for the credentials
        final Binding binding = bindingProvider.getBinding();
        List<Handler> handlerList = binding.getHandlerChain();
        if (handlerList == null)
            handlerList = new ArrayList<Handler>();

        handlerList.add(new SecurityHandler());
        binding.setHandlerChain(handlerList);

        // Set the actual web services address instead of the mock service
        Map<String, Object> requestContext = bindingProvider.getRequestContext();
        requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://"+ip+"/onvif/device_service"); 

        // Read the time from the server
        SystemDateTime systemDateAndTime = device.getSystemDateAndTime();
        // Mark the local time (no need for an actual clock, the monotone counter will do just fine)
        clientTime = System.nanoTime()/1000000;

        // Generate the server time in msec since epoch
        DateTime utcDateTime = systemDateAndTime.getUTCDateTime();
        org.onvif.ver10.schema.Date date = utcDateTime.getDate();
        Time time = utcDateTime.getTime();
        Calendar c = new GregorianCalendar(utc);
        c.set(date.getYear(), date.getMonth()-1, date.getDay(), time.getHour(), time.getMinute(), time.getSecond());
        System.out.println(sdf.format(c.getTime()));
        serverTime = c.getTimeInMillis();

        // Now try and read something interesting
        Holder<String> manufacturer = new Holder<String>();
        Holder<String> model = new Holder<String>();
        Holder<String> firmwareVersion = new Holder<String>();
        Holder<String> serialNumber = new Holder<String>();
        Holder<String> hardwareId = new Holder<String>();
        device.getDeviceInformation(manufacturer, model, firmwareVersion, serialNumber, hardwareId);
        System.out.println(manufacturer.value);
        System.out.println(model.value);
        System.out.println(firmwareVersion.value);
        System.out.println(serialNumber.value);
        System.out.println(hardwareId.value);
    }

    // Calcualte the password digest from a concatenation of the nonce, the creation time and the password itself
    private static String calculatePasswordDigest(byte[] nonceBytes, String created, String password) {
        String encoded = null;
        try {
            MessageDigest md = MessageDigest.getInstance( "SHA1" );
            md.reset();
            md.update( nonceBytes );
            md.update( created.getBytes() );
            md.update( password.getBytes() );
            byte[] encodedPassword = md.digest();
            encoded = Base64.encode(encodedPassword);
        } catch (NoSuchAlgorithmException ex) {
        }

        return encoded;
    }

    // Calculate what time is it right now on the server
    private static String localToGmtTimestamp() {
        return sdf.format(new Date(System.nanoTime()/1000000 - clientTime + serverTime));
    }

    // This handler will add the authentication parameters
    private static final class SecurityHandler implements SOAPHandler<SOAPMessageContext> {

        @Override
        public boolean handleMessage(final SOAPMessageContext msgCtx) {

            // Indicator telling us which direction this message is going in
            final Boolean outInd = (Boolean) msgCtx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

            // Handler must only add security headers to outbound messages
            if (outInd.booleanValue() && clientTime!=0 && user!=null && pass!=null) {
                try {
                    // Create the timestamp
                    String timestamp = localToGmtTimestamp();

                    // Generate a random nonce
                    byte[] nonceBytes = new byte[16]; 
                    for (int i=0 ; i<16 ; ++i)
                        nonceBytes[i] = (byte)(Math.random()*256-128);

                    // Digest
                    String dig=calculatePasswordDigest(nonceBytes, timestamp, pass);

                    // Create the xml
                    SOAPEnvelope envelope = msgCtx.getMessage().getSOAPPart().getEnvelope();
                    SOAPHeader header = envelope.getHeader();
                    if (header == null)
                        header = envelope.addHeader();

                    SOAPElement security =
                    header.addChildElement("Security", "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");

                    SOAPElement usernameToken =
                    security.addChildElement("UsernameToken", "wsse");

                    SOAPElement username =
                    usernameToken.addChildElement("Username", "wsse");
                    username.addTextNode(user);

                    SOAPElement password =
                    usernameToken.addChildElement("Password", "wsse");
                    password.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest");
                    password.addTextNode(dig);

                    SOAPElement nonce =
                    usernameToken.addChildElement("Nonce", "wsse");
                    nonce.setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
                    nonce.addTextNode(Base64.encode(nonceBytes));

                    SOAPElement created = usernameToken.addChildElement("Created", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
                    created.addTextNode(timestamp);

                } catch (final Exception e) {
                    e.printStackTrace();
                    return false;
                }
            }
            return true;
        }

        // Other required methods on interface need no guts

        @Override
        public boolean handleFault(SOAPMessageContext context) {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public void close(MessageContext context) {
            // TODO Auto-generated method stub

        }

        @Override
        public Set<QName> getHeaders() {
            // TODO Auto-generated method stub
            return null;
        }
    }

}
person Shloim    schedule 01.05.2012
comment
Код выше обсуждается более подробно в эта ветка JavaRanch. - person Andrea; 30.05.2014
comment
Приведенная выше ссылка показывает весь процесс, который мы тщательно прошли, чтобы найти ответ. Он довольно полезен для понимания трудностей и по той же причине имеет много ошибок. Представленный здесь код компилируется и работает на нескольких камерах ONVIF. - person Shloim; 18.08.2014

Я бы рекомендовал использовать команду wsimport для создания клиента веб-службы для использования веб-служб.

Команду можно выполнить из командной строки cmd,


wsimport -d D:\WS-Client -extension -keep -XadditionalHeaders http://path-to-your-webserbice-wsdl-file/sampleWSDL?wsdl

После выполнения вышеуказанной команды все сгенерированные файлы .class и .java (исходные) файлы будут помещены в папку D:\WS-Client с правильной структурой пакета, как указано в файле wsdl.

просто игнорируйте файлы .class и скопируйте всю папку пакета и включите ее в свой потребительский проект, чтобы использовать ее.

Это будет похоже на то, что у вас есть развернутые веб-сервисы в исходном коде. Просто вызовите методы из сервисных классов и охла :)

person Prashant Jadhav    schedule 13.03.2014

Предоставленный вам WSDL недействителен. Скорее всего из-за обширных тегов документации, которые использовались в нем. Вы можете убедиться в этом, попробовав загрузить его в SoapUI. Лучше всего связаться с поставщиком, чтобы узнать, есть ли у него более чистая версия WSDL, которую они могут вам предоставить.

person Perception    schedule 12.01.2012
comment
Означает ли это, что он будет работать, если я удалю тег документации? Я не уверен в этом?!!. и почему он работает с использованием Visual Studio? - person Mike Albren; 12.01.2012

сначала вы хотите развернуть свой проект веб-службы на любом сервере, означает tomcat или другой. после этого используйте URL-адрес файла WSDL работающего сервера для создания клиента.

person Rakesh Patel    schedule 10.01.2012
comment
Зачем мне создавать проект веб-сервиса на сервере? Северная часть уже развернута на IP-камере от производителя. Не могли бы вы объяснить больше? - person Mike Albren; 10.01.2012
comment
из-за того, что клиент веб-службы может выполнять файл wsdl, и когда вы указываете локальный путь, среда выполнения не существует, поэтому нам нужно развернуть на сервере и использовать URL-адрес сервера для создания клиента. - person Rakesh Patel; 10.01.2012
comment
Как я могу создать файлы Java из wsdl? Вы рекомендуете какой-либо фреймворк использовать его для реализации клиента - person Mike Albren; 10.01.2012
comment
клиент веб-службы thw автоматически создает файл java из файла wsdl, и вы можете использовать ось для создания файла java из файла wsdl. - person Rakesh Patel; 10.01.2012