プログラマ38の日記

主にプログラムメモです。

【Java】wsimportでSalesforce APIを使用する

まずはサンプルコード

    Soap soapBinding = new SforceService().getSoap();
    
    WSBindingProvider provider = (WSBindingProvider)soapBinding;
    
    Map<String, Object> reqContext = provider.getRequestContext();
    reqContext.put(WSBindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://login.salesforce.com/services/Soap/u/39.0");
    reqContext.put("javax.xml.ws.client.connectionTimeout", "180");
    reqContext.put("javax.xml.ws.client.receiveTimeout", "180");
    
    //Enable GZip compression
    Map<String, List<String>> httpHeaders = new HashMap<String, List<String>>();
    httpHeaders.put("Content-Encoding", Collections.singletonList("gzip"));
    httpHeaders.put("Accept-Encoding", Collections.singletonList("gzip"));
    reqContext.put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders);
        
    //httpプロキシを指定する
    {
      System.setProperty("https.proxyHost", "samplehost");
      System.setProperty("https.proxyPort", "9999");
      ProxyUserPasswordConfig.setProxyUserPassword("sampleproxyuser", "sampleproxypasword");
    }
        
    LoginResult loginResult = soapBinding.login("sampleuser@sample.username", "samplepassword");
    
    String serverUrl = loginResult.getServerUrl();
    
    reqContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serverUrl);
    
    SessionHeader sh = new SessionHeader();
    String sessionId = loginResult.getSessionId();
    sh.setSessionId(sessionId);
    
    JAXBContext jaxbContext = null;
    try {
        jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
    } catch (JAXBException e) {}

    ArrayList<Header> sessionheaderlst = new ArrayList<Header>();
    sessionheaderlst.add(Headers.create((JAXBContext) jaxbContext, sh));
    provider.setOutboundHeaders(sessionheaderlst);
    
    System.out.println(soapBinding.getServerTimestamp().getTimestamp());

 httpproxyのユーザ、パスワードは「PasswordAuthentication」を使うため上記で次のクラスを使っています。※他のやり方もあるとは思いますがこのやり方にしました。

import java.net.Authenticator;
import java.net.PasswordAuthentication;

public class ProxyUserPasswordConfig extends Authenticator {
  
  private String user;
  private String password;
  
  private ProxyUserPasswordConfig(String proxyuser,  String proxypassword){
    user = proxyuser;
    password = proxypassword;
  }

  public static void setProxyUserPassword(String proxyuser, String proxypassword){    
    Authenticator.setDefault(new ProxyUserPasswordConfig(proxyuser,proxypassword));
  }
    protected PasswordAuthentication getPasswordAuthentication() {  
        return new PasswordAuthentication(user, password.toCharArray()); 
    }  
}

コードの生成とライブラリの作成

jdk付属のwsimportコマンドを使います。wsdlを含んだjarまで自動で作成してくれます。

mkdir partner
mkdir metadata
wsimport -B-XautoNameResolution -d partner  -clientjar partner.jar  -b _jaxb.xml partner.wsdl
wsimport -B-XautoNameResolution -d metadata -clientjar metadata.jar -b _jaxb.xml metadata.wsdl

_jaxb.xmlというのを上記コマンドで指定していますが、_jaxb.xmlの中身は次のテキストです。 typesafeEnumMaxMembersの上限エラーになったのでサイズを増やしています。

<jaxb:bindings
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    jaxb:extensionBindingPrefixes="xjc"
    jaxb:version="1.0">
    <jaxb:globalBindings typesafeEnumMaxMembers="2000">
  <xjc:serializable/>
    </jaxb:globalBindings>
</jaxb:bindings>

httpproxyの設定

proxyhostとproxyportはシステムプロパティで指定するのですが、proxyuserとproxypasswordはコード内で指定となっています。

      System.setProperty("https.proxyHost", "samplehost");
      System.setProperty("https.proxyPort", "9999");
      ProxyUserPasswordConfig.setProxyUserPassword("sampleproxyuser", "sampleproxypasword");

エンドポイントの設定

wsimportを標準のまま使うとエンドポイントが変更できません。そのため外部のライブラリJAXWS-RIを使います。(現在まとめてダウンロードできないのでこちらにアップしました。)このライブラリ内のWSBindingProviderを使います。

    WSBindingProvider provider = (WSBindingProvider)soapBinding;
    
    Map<String, Object> reqContext = provider.getRequestContext();
    reqContext.put(WSBindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://login.salesforce.com/services/Soap/u/39.0");

セッションIDと、インスタンスURLのセット

ここも、エンドポイントと同様に、WSBindingProviderを使います。

やりたいことは、SOAPHeaderにセッションヘッダをつけて、エンドポイントを変えたいだけなのに、結構書く必要があります。

    String serverUrl = loginResult.getServerUrl();
    
    reqContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serverUrl);
    
    SessionHeader sh = new SessionHeader();
    String sessionId = loginResult.getSessionId();
    sh.setSessionId(sessionId);
    
    JAXBContext jaxbContext = null;
    try {
        jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
    } catch (JAXBException e) {}

    ArrayList<Header> sessionheaderlst = new ArrayList<Header>();
    sessionheaderlst.add(Headers.create((JAXBContext) jaxbContext, sh));
    provider.setOutboundHeaders(sessionheaderlst);

 これで上記コードのsoapBindingでSOAP APIを使用できます。

以前(java6頃)のwsimportはデータバインディングが非常に遅かったのですが、java8ではかなり改善しているように思います。