3

I need to create web service client in Java using Eclipse the consumes the onvif wsdl.

I spent several hours without finding a how to do that, this the first time I am using soap, my experience was in REST.

I tried many tutorials like this to create web service client, but when I am trying to choose the wsdl file from my local disk, eclipse shows the an error Could not retrieve the WSDL file ..., the link structure I used for the file was file:/C:/ONVIF/media.wsdl.

I need to use any Java framework that support WS-Notification to implement my client.

Can you please tell me how to implement client web service that consumes the WSDL files.
Do I need web server to implement soap web service client?
If yes, why?

logi-kal
  • 7,107
  • 6
  • 31
  • 43
Mike Albren
  • 203
  • 3
  • 6
  • 15

4 Answers4

5

Here is a complete code and guide on how to consume one of ONVIF's wsdl files (devicemgmt.wsdl) and how to use it to connect to a device:

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

}
Shloim
  • 5,281
  • 21
  • 36
  • The code above is discussed in more depth in [this JavaRanch thread](http://www.coderanch.com/t/562144/Web-Services/java/Generating-client-code-wsdl-file). – Andrea May 30 '14 at 09:08
  • The above link show the entire process we went thorough in order to find the answer. It is quite useful for understanding the difficulties and for the same reason has a lot of mistakes. The code presented here compiles and runs on several ONVIF cameras. – Shloim Aug 18 '14 at 15:03
2

I would recommend using wsimport command to generate the web service client to consume the web services.

The command can be executed from cmd prompt,


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

After execution of the above command all the generated .class files and .java (source) files will be placed inside D:\WS-Client folder with proper package structure as mentioned in the wsdl file.

just ignore the .class files and copy entire package folder and include it in your consumer project to use it.

It will be like, you have the deployed web services in your source code. Just call the methods from the service classes and ohhla :)

1

The WSDL you were provided is invalid. Most likely due to the extensive documentation tags that were used in it. You can verify this by trying to load it in SoapUI. Your best bet is to contact the vendor to find out if they have a cleaner version of the WSDL they can provide you.

Perception
  • 79,279
  • 19
  • 185
  • 195
  • Does this mean it will work if I remove the documentation tag? I am not sure about this ?!!. and why it is working using Visual studio? – Mike Albren Jan 12 '12 at 18:34
0

first you want to deploy your web service project on any server means tomcat or other. after that use the running server WSDL file URL for create the client.

Rakesh Patel
  • 393
  • 2
  • 10
  • 1
    Why do I need to create web service project on a server? The sever part is already deployed on the IP camera from the manufacturer. Can you please explain more? – Mike Albren Jan 10 '12 at 04:31
  • because of the webservice client is tru to execute the wsdl file and when you specify the local path then no any execution environment is exists so we need to deploy on server and use the server URL for create the client. – Rakesh Patel Jan 10 '12 at 04:47
  • How can I create the java files from the wsdl ? do you recommend any framework to use it to implement the client – Mike Albren Jan 10 '12 at 05:06
  • thw web service client is automatically create the java file from wsdl file and you can use the axis for create java file from wsdl file. – Rakesh Patel Jan 10 '12 at 05:14