[JAVA] Utilisation SOAPI / API OVH

[Fermé]
Signaler
Messages postés
181
Date d'inscription
dimanche 23 septembre 2012
Statut
Membre
Dernière intervention
9 août 2016
-
Messages postés
16400
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 septembre 2021
-
Bonjour,

Je suis en train d'essayer de créer une connexion en java sur mon serveur OVH grâce à leur api mais je n'arrive pas a voir quel est le rapport avec ce fichier https://www.ovh.com/soapi/soapi-dlw-latest.wsdl mais apparemment j'ai besoin de l'intégrer dans mon code :
package net.ovh.vps*****.api;

import com.ovh.soapi.manager.DedicatedInfoReturn;
import com.ovh.soapi.manager.ManagerPortType;
import com.ovh.soapi.manager.ManagerService;

public class Main {

 public static void main(String[] args) {

  
  ManagerPortType soapi = new ManagerService().getManagerPort();

  // Login
  String session = soapi.login("********-ovh", "********", "fr", false);

  // DedicatedInfo
  DedicatedInfoReturn result = soapi.dedicatedInfo(session,
    "vps*****.ovh.net");
  System.out.println(result);

  // Logout
  soapi.logout(session);
 }
}


Est-ce que quelqu'un pourrai m'aider svp ?

2 réponses

Messages postés
16400
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 septembre 2021
2 879
Bonjour,

Un WSDL permet de décrire avec un web service SOAP, alors je ne connais pas spécifiquement l'API d'OVH mais j'imagine que c'est une surcouche Java qui permet d'établir les connexions avec les différents web services.

Par exemple au début du WSDL tu as des définitions de types XML qui pourront être échangés entre l'application cliente et le web service.

<xsd:element name="mailingListModeratorDel">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="session" type="xsd:string" nillable="true" />
      <xsd:element name="domain" type="xsd:string" nillable="true" />
      <xsd:element name="ml" type="xsd:string" nillable="true" />
      <xsd:element name="email" type="xsd:string" nillable="true" />
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

Il y a fort à parier que dans l'API Java tu es une classe MailingListModeratorDel ou avec un nom proche, qui aura pour attributs des String session, domain, ml et email.

Puis dans la deuxième moitié du WSDL tu as les différents services accessibles et leurs paramètres. Par exemple tout à la fin :

<wsdl:service name="managerService">
  <wsdl:port name="managerPort" binding="wsdlns:managerBinding">
    <soap:address location="https://www.ovh.com:1664" />
  </wsdl:port>
</wsdl:service>

Tu auras là aussi une classe quelque part dans ton API Java une méthode qui prendra en paramètre un objet managerBinding appelé managerPort et qui dans son code fait un appel web service managerService...

Le WSDL c'est la base du web service, il est forcément nécessaire pour permettre le web service. Après il y a l'API Java qui donne des facilités pour utiliser le web service, j'imagine qu'il doit y a avoir des API dans d'autres langages, et si pour je ne sais quel raison tu voudrais utiliser le web service sans aucune API tu pourrais le faire sans problème, il suffit juste de connaître l'emplacement du WSDL.

Tu peux par exemple installer SoapUI, un logiciel qui permet de faire des requêtes sur n'importe quel web service directement de XML à XML. Il suffit juste de lui indiquer le WSDL pour qu'il aille lire les différents services et types de données utilisables, et il te pré-rempli le XML de chaque requête, il faut juste remplacer les points d'interrogations.
Messages postés
181
Date d'inscription
dimanche 23 septembre 2012
Statut
Membre
Dernière intervention
9 août 2016
77
Oui c'est ce que je me suis dit aussi (mais en mode plus bordélique dans ma tête xD) et comme je suis pas un expert en java --'.
J'ai téléchargé l'api a cette adresse : https://soapi-jaxws-client.googlecode.com/files/ovh-soapi-client-1.26.jar
Messages postés
16400
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 septembre 2021
2 879
Quand on regarde le code, on a ça :

package com.ovh.soapi.manager;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;

@WebServiceClient(name="managerService", targetNamespace="http://soapi.ovh.com/manager", wsdlLocation="http://www.ovh.com/soapi/*")
public class ManagerService extends Service
{ 
  private static final Logger logger = Logger.getLogger(ManagerService.class.getName()); // ligne 26

  static
  {
    URL localURL1 = null; // ligne 29
    try
    {
      URL localURL2 = ManagerService.class.getResource("."); // ligne 32
      localURL1 = new URL(localURL2, "http://www.ovh.com/soapi/*"); // ligne 33
    } catch (MalformedURLException localMalformedURLException) {
      logger.warning("Failed to create URL for the wsdl Location: 'http://www.ovh.com/soapi/*', retrying as a local file"); // ligne 35
      logger.warning(localMalformedURLException.getMessage()); // ligne 36
    }
  }

  private static final URL MANAGERSERVICE_WSDL_LOCATION = localURL1; // ligne 38
  
  public ManagerService(URL paramURL, QName paramQName)
  {
    super(paramURL, paramQName); // ligne 42
  }

  public ManagerService() {
    super(MANAGERSERVICE_WSDL_LOCATION, new QName("http://soapi.ovh.com/manager", "managerService")); // ligne 46
  }

  @WebEndpoint(name="managerPort")
  public ManagerPortType getManagerPort()
  {
    return (ManagerPortType)super.getPort(new QName("http://soapi.ovh.com/manager", "managerPort"), ManagerPortType.class); // ligne 56
  }

  @WebEndpoint(name="managerPort")
  public ManagerPortType getManagerPort(WebServiceFeature[] paramArrayOfWebServiceFeature)
  {
    return (ManagerPortType)super.getPort(new QName("http://soapi.ovh.com/manager", "managerPort"), ManagerPortType.class, paramArrayOfWebServiceFeature); // ligne 68
  }
}

Cela correspond justement à la toute fin du WSDL que je donnais tout à l'heure :

<wsdl:service name="managerService">
  <wsdl:port name="managerPort" binding="wsdlns:managerBinding">
    <soap:address location="https://www.ovh.com:1664" />
  </wsdl:port>
</wsdl:service>

Vu de la manière dont c'est fait, c'est normal d'avoir "https://www.ovh.com/soapi/*" vu que c'est codé en dur dans le code. En fait je pense que l'API a été généré automatiquement à partir d'une version précédente du WSDL et que ce n'est pas compatible.

S'il y avait juste à changer la valeur de la constante MANAGERSERVICE_WSDL_LOCATION pour qu'elle accepte une nouvelle URL alors on peut faire un petit patch comme ça.
Chez moi ça permet de passer le constructeur sans erreur :

package test;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;

import com.ovh.soapi.manager.ManagerService;

public class Test
{
    /**
     * @param newManagerServiceWsdlLocation
     * @return previousManagerServiceWsdlLocation
     */
    public static String patchManagerService(String newManagerServiceWsdlLocation) throws MalformedURLException,NoSuchFieldException, SecurityException,IllegalArgumentException,IllegalAccessException
    {
        URL localURL2 = ManagerService.class.getResource(".");
        URL localURL1 = new URL(localURL2, newManagerServiceWsdlLocation);
        
        Field declaredField = ManagerService.class.getDeclaredField("MANAGERSERVICE_WSDL_LOCATION");
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        
        declaredField.setAccessible(true);
        modifiersField.setAccessible(true);
        modifiersField.setInt(declaredField, declaredField.getModifiers() & ~Modifier.FINAL);
        
        URL previousManagerServiceWsdlLocation = (URL) declaredField.get(null);
        declaredField.set(null, localURL1);
        
        modifiersField.setInt(declaredField, declaredField.getModifiers() & ~Modifier.FINAL);
        declaredField.setAccessible(false);
        modifiersField.setAccessible(false);
        
        return previousManagerServiceWsdlLocation.toString();
    }
    
    public static void main(String[] args) throws Exception
    {
        patchManagerService("http://www.ovh.com/soapi/soapi-dlw-latest.wsdl");
        ManagerService mService = new ManagerService(); // OK
    }
}

Malheureusement ça plante ensuite, lorsqu'on essaye de faire le getManagerPort :

Exception in thread "main" javax.xml.ws.WebServiceException: Method telephonyConferenceAnnounceDelete is exposed as WebMethod, but there is no corresponding wsdl operation with name telephonyConferenceAnnounceDelete in the wsdl:portType{http://soapi.ovh.com/manager}managerPortType
 at com.sun.xml.internal.ws.model.JavaMethodImpl.freeze(JavaMethodImpl.java:341)
 at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.freeze(AbstractSEIModelImpl.java:94)
 at com.sun.xml.internal.ws.model.RuntimeModeler.buildRuntimeModel(RuntimeModeler.java:240)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.createSEIPortInfo(WSServiceDelegate.java:672)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.addSEI(WSServiceDelegate.java:660)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:329)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:312)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:294)
 at javax.xml.ws.Service.getPort(Service.java:119)
 at com.ovh.soapi.manager.ManagerService.getManagerPort(ManagerService.java:56)


En effet le code de la classe ManagerPort est comme ceci dans l'API :

package com.ovh.soapi.manager;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;

@WebService(name="managerPortType", targetNamespace="http://soapi.ovh.com/manager")
@XmlSeeAlso({ObjectFactory.class})
public abstract interface ManagerPortType
{
  // ...

  @WebMethod
  @RequestWrapper(localName="telephonyConferenceAnnounceDelete", targetNamespace="http://soapi.ovh.com/manager", className="com.ovh.soapi.manager.TelephonyConferenceAnnounceDelete")
  @ResponseWrapper(localName="telephonyConferenceAnnounceDeleteResponse", targetNamespace="http://soapi.ovh.com/manager", className="com.ovh.soapi.manager.TelephonyConferenceAnnounceDeleteResponse")
  public abstract void telephonyConferenceAnnounceDelete(@WebParam(name="session", targetNamespace="http://soapi.ovh.com/manager") String paramString1, @WebParam(name="number", targetNamespace="http://soapi.ovh.com/manager") String paramString2, @WebParam(name="country", targetNamespace="http://soapi.ovh.com/manager") String paramString3, @WebParam(name="room", targetNamespace="http://soapi.ovh.com/manager") String paramString4, @WebParam(name="announceType", targetNamespace="http://soapi.ovh.com/manager") String paramString5);

  // ...  
}

Or "telephonyConferenceAnnounceDelete" n'est pas définie dans le WSDL, il est donc impossible que ça fonctionne. Je pense que l'API est vraiment trop ancienne, dans les liens de téléchargement ça indique une dernière version en novembre 2011, or le web service de OVH a surement changé depuis, cet API ne peut donc pas être utilisée. Il faudrait trouver l'API officielle la plus récente.
Messages postés
181
Date d'inscription
dimanche 23 septembre 2012
Statut
Membre
Dernière intervention
9 août 2016
77
Ok donc il faudrait que je vois si il y a pas une maj de l'api ?
Messages postés
16400
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 septembre 2021
2 879
En effet, je pense que ce n'est pas la bonne API.

Quand on suit le lien https://www.ovh.com/soapi/* on tombe sur la page de "spécifications SoAPI", or depuis le site d'OVH on ne tombe là dessus que dans la FAQ en suivant le lien tout en bas "Looking for the old OVH SoAPI ?"

La nouvelle API telle que décrite sur https://api.ovh.com/console/ n'a même pas l'air d'être en SOAP, la technologie a changé, il n'y a même plus de WSDL ou de XML, c'est du REST.

Cependant il n'y a pas de wrapper en Java pour cet API, uniquement Perl, Python, et PHP. Mais elles sont super courtes (une centaine de lignes à peine), rien à voir avec le gros JAR de l'API Soap. Donc les reprendre en Java ne devrait pas être trop compliqué (le REST est beaucoup plus simple à manipuler que le SOAP)
Messages postés
181
Date d'inscription
dimanche 23 septembre 2012
Statut
Membre
Dernière intervention
9 août 2016
77
Ok merci
Messages postés
16400
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 septembre 2021
2 879
Je n'ai pas de compte OVH alors je n'ai pas pu tester, mais voici une conversion vite faite des API PHP/Python de OVH en Java. Il y a sûrement des bugs, mais c'est un point de départ... Si tu as l'occasion de l'utiliser et de corriger les bugs, n'hésite pas à partager le code correct ;-)

Remarque : il n'y a pas de convertisseur JSON standard en Java, du coup j'ai juste mis une interface JsonParser, il faudra l'implémenter pour pouvoir passer d'un String à un objet Json et inversement. On pourra utiliser des bibliothèques comme google-gson, Jackson ou bien d'autres.

package test;

import ovh.api.JsonParser;
import ovh.api.OvhApi;
import ovh.api.OvhApi.Cluster;

public class Test
{
    private static class JsonParserTest implements JsonParser<String, String>
    {
        @Override
        public String convert(String json)
        {
            return json;
        }
    };
    
    public static void main(String[] args)
    {
        OvhApi<String, String> ovh = new OvhApi<String, String>(Cluster.OVH_API_EU, "applicationKey", "applicationSecret", "consumerKey", new JsonParserTest());
        
        String result = ovh.get("/me", null);
        
        System.out.println(result);
    }
}

package ovh.api;

public interface JsonParser<JsonIn, JsonOut>
{
    /**
     * @param json
     *            Json Java object
     * @return Json String
     */
    public String convert(JsonIn json);
    
    /**
     * @param json
     *            Json String
     * @return Json Java object
     */
    public JsonOut convert(String json);
}

package ovh.api;

import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Scanner;

public class OvhApi<JsonIn, JsonOut>
{
    public static enum Cluster
    {
        OVH_API_EU("https://api.ovh.com/1.0"),
        OVH_API_CA("https://ca.api.ovh.com/1.0");
        
        private final String root;
        
        private Cluster(String root)
        {
            this.root = root;
        }
        
        public String getRoot()
        {
            return root;
        }
    }
    
    private final String applicationKey;
    private final String applicationSecret;
    private final String consumerKey;
    private final Cluster root;
    private final long timeDelta;
    
    private final JsonParser<JsonIn, JsonOut> parser;
    
    private static final String AUTH_TIME = "/auth/time";
    
    /**
     * Construct a new wrapper instance.
     * 
     * @param root
     *            the ovh cluster you want to call
     * @param applicationKey
     *            your application key given by OVH on application registration
     * @param applicationSecret
     *            your application secret given by OVH on application registration
     * @param consumerKey
     *            the consumer key you want to use, if any, given after a credential request
     */
    public OvhApi(Cluster root, String applicationKey, String applicationSecret, String consumerKey, JsonParser<JsonIn, JsonOut> parser)
    {
        this.applicationKey = applicationKey;
        this.applicationSecret = applicationSecret;
        this.consumerKey = consumerKey;
        this.root = root;
        
        this.timeDelta = timeDelta();
        
        this.parser = parser;
    }
    
    /**
     * Get the delta between this computer and the OVH cluster to sign further queries
     * 
     * @return delta in seconds
     */
    private long timeDelta()
    {
        try
        {
            Scanner sc = new Scanner(new URL(root.getRoot() + AUTH_TIME).openStream());
            long serverTime = sc.nextLong();
            sc.close();
            
            long currentTime = new Date().getTime() / 1000;
            
            return currentTime - serverTime;
        }
        catch (Exception e)
        {
            throw new RuntimeException("Can't compute time drift for " + root, e);
        }
    }
    
    /**
     * This is the main method of this wrapper. It will sign a given query and return its result.
     * 
     * @param method
     *            the HTTP method of the request (get/post/put/delete)
     * @param path
     *            the url you want to request
     * @param content
     *            the object you want to send in your request (will be automatically serialized to JSON)
     * @return
     */
    private JsonOut call(String method, String path, JsonIn content)
    {
        String url = root.getRoot() + path;
        
        try
        {
            String body = content == null ? "" : parser.convert(content);
            
            long now = new Date().getTime() / 1000 + timeDelta;
                        
            URL obj = new URL(url);
            HttpURLConnection con = (HttpURLConnection) obj.openConnection();
            
            con.setRequestMethod(method);
            con.setRequestProperty("Content-Type", "application/json");
            con.setRequestProperty("X-Ovh-Application", applicationKey);
            con.setRequestProperty("X-Ovh-Consumer", consumerKey);
            con.setRequestProperty("X-Ovh-Signature", "$1$" + hash(applicationSecret + "+" + consumerKey + "+" + url + "+" + body + "+" + now));
            con.setRequestProperty("X-Ovh-Timestamp", String.valueOf(now));
            
            if (!body.isEmpty())
            {
                con.setDoOutput(true);
                con.setRequestProperty("Content-Length", Integer.toString(body.length()));
                con.getOutputStream().write(body.getBytes("UTF8"));
            }
            
            StringBuilder response = new StringBuilder();
            
            Scanner sc = new Scanner(con.getInputStream());
            
            while (sc.hasNextLine())
                response.append(sc.nextLine());
            
            sc.close();
            
            return parser.convert(response.toString());
        }
        catch (Exception e)
        {
            throw new RuntimeException("Can't call " + method + " on " + url, e);
        }
    }
    
    private static String hash(String message) throws NoSuchAlgorithmException
    {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(message.getBytes());
        
        byte byteData[] = md.digest();
        
        StringBuilder sb = new StringBuilder();
        
        for (int i = 0; i < byteData.length; i++ )
            sb.append(Integer.toString( (byteData[i] & 0xff) + 0x100, 16).substring(1));
        
        return sb.toString();
    }
    
    public JsonOut get(String path, JsonIn content)
    {
        return call("GET", path, content);
    }
    
    public JsonOut put(String path, JsonIn content)
    {
        return call("PUT", path, content);
    }
    
    public JsonOut post(String path, JsonIn content)
    {
        return call("POST", path, content);
    }
    
    public JsonOut delete(String path, JsonIn content)
    {
        return call("DELETE", path, content);
    }    
}
Messages postés
181
Date d'inscription
dimanche 23 septembre 2012
Statut
Membre
Dernière intervention
9 août 2016
77
Bon avec le /domain sur la console de l'api d'ovh je peut obtenir ces informations :
http://puu.sh/9Hcnt/67906e3d73.png

Et il n'y a pas de "s" a /domain
Messages postés
16400
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 septembre 2021
2 879
Au temps pour moi, j'avais mis un "s" en suivant leur exemple.

Et comment as tu fait pour que ça fonctionne ? Uniquement en prenant l'outil de leur site ?
Ça veut dire que tes identifiants sont correct et que c'est donc mon code qui est faux :(
Messages postés
181
Date d'inscription
dimanche 23 septembre 2012
Statut
Membre
Dernière intervention
9 août 2016
77
Quand j'utilise la console sur leur site ils me génèrent automatiquement des identifiants
Messages postés
181
Date d'inscription
dimanche 23 septembre 2012
Statut
Membre
Dernière intervention
9 août 2016
77
C'est pour ça que je peux les avoir
Messages postés
16400
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 septembre 2021
2 879
Ok, je regarderais à l'occasion.