プログラマ38の日記

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

Salesforce: コマンドラインのメタデータのエクスポートツールのAPIバージョンをあげました(apiver40.0)

Summer'17(apiver40.0)のエクスポートツールはこちらです。

 

api39.0のものや、使い方は前回の記事を参照ください。

 

crmprogrammer38.hatenablog.com

 

修正履歴

2017/7/24 CustomFeedFilterのダウンロード時にエラーとなる事象の対応を行いました。具体的には、CustomFeedFilterはワイルドカード指定でリクエストするように変更しています。

Java: wscでSalesforce APIを使用する

まずはサンプルコード

    ConnectorConfig connectorConfig = new ConnectorConfig();
String soapEndpoint = "https://login.salesforce.com/services/Soap/u/39.0";
connectorConfig.setAuthEndpoint(soapEndpoint);
connectorConfig.setManualLogin(true);
connectorConfig.setCompression(true);
connectorConfig.setConnectionTimeout(120000);

//httpプロキシを指定する
{
connectorConfig.setProxy("samplehost", 9999);
ProxyUserPasswordConfig.setProxyUserPassword("sampleproxyuser", "sampleproxypasword");
}

PartnerConnection soapBinding = Connector.newConnection(connectorConfig);
LoginResult lr = soapBinding.login("sampleuser@sample.username", "samplepassword");
soapBinding.setSessionHeader(lr.getSessionId());
connectorConfig.setServiceEndpoint(lr.getServerUrl());

System.out.println(soapBinding.getServerTimestamp().getTimestamp());

httpproxyのユーザ、パスワードは「PasswordAuthentication」を使うため上記で次のクラスを使っています。(wsimportの時と同様です)

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

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

wscのライブラリをclasspathに指定し次のコマンドを使います。jarまで自動で作成してくれます。jarにはソースコードも含まれます。

java -DcompileTarget=1.8  -classpath ..\lib\* com.sforce.ws.tools.wsdlc partner.wsdl partner.jar
java -DcompileTarget=1.8 -classpath ..\lib\* com.sforce.ws.tools.wsdlc metadata.wsdl metadata.jar

httpproxyの設定

コード内で指定となっています。

      connectorConfig.setProxy("samplehost", 9999);
ProxyUserPasswordConfig.setProxyUserPassword("sampleproxyuser", "sampleproxypasword");

エンドポイントの設定

ConnectorConfigで指定となります。

    ConnectorConfig connectorConfig = new ConnectorConfig();
String soapEndpoint = "https://login.salesforce.com/services/Soap/u/39.0";
connectorConfig.setAuthEndpoint(soapEndpoint);

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

ここも、エンドポイントと同様に、ConnectorConfigで指定します。

    soapBinding.setSessionHeader(lr.getSessionId());
connectorConfig.setServiceEndpoint(lr.getServerUrl());

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

Salesforce専用ライブラリであり、他のライブラリに比べとても簡潔なコードになります。

また、スレッドセーフな設計です。(他のライブラリは1つのスタブインスタンスを複数スレッドで使うことができません)

 

ただし、注意事項として、insert/update/upsertで、ライブラリ側で項目にセットした値の型をチェックします。

他のライブラリは項目にセットするのは文字型であり、

  • 日付型はyyyy-MM-ddの形式
  • 日付/時間型はyyyy-MM-dd'Y'HH:mm:ss.000Z形式
  • チェックボックス型はtrue/false
  • バイナリ型はbase64エンコードされた文字列
  • 数値型は、数値として扱える文字列(指数表現もok)

なのに対して、wscは

  • 日付型はDate
  • 日付/時間型はCalendar
  • チェックボックス型はBoolean
  • バイナリ型はbyte[]
  • int型は、Integer (なぜか他の数値項目は、数値として扱える文字列でok)

の制限があります。

 

他のライブラリでは、上記型の値が不正だった場合、APIコール時に原因追求が困難なエラーがでます。が、wscはライブラリ側であえてエラー制御を実装しているということだと思います。

 

Java: apache cxfでSalesforce APIを使用する

まずはサンプルコード

    URL wsdlurl = Soap.class.getClassLoader().getResource("/partner.wsdl");
Soap soapBinding = new SforceService(wsdlurl).getSoap();

BindingProvider bp = (BindingProvider)soapBinding;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,"https://login.salesforce.com/services/Soap/u/38.0");

Client client = ClientProxy.getClient(bp);

client.getInInterceptors().add(new GZIPInInterceptor());
client.getOutInterceptors().add(new GZIPOutInterceptor());

HTTPConduit conduit = (HTTPConduit)client.getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
policy.setAllowChunking(true);
policy.setConnectionTimeout(120000);
policy.setProxyServer("sampleproxyhost");
policy.setProxyServerType(ProxyServerType.HTTP);
policy.setProxyServerPort(9999);

ProxyAuthorizationPolicy authpolicy = new ProxyAuthorizationPolicy();
authpolicy.setUserName("sampleproxyuser");
authpolicy.setPassword("samplepassword");
conduit.setProxyAuthorization(authpolicy);

conduit.setClient(policy);


LoginResult loginResult = soapBinding.login("sampleuser@sample.username", "samplepassword", null, null);


bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,loginResult.getServerUrl());

SessionHeader sh = new SessionHeader();
String sessionId = loginResult.getSessionId();
sh.setSessionId(sessionId);

System.out.println(soapBinding.getServerTimestamp(sh, null, null).getTimestamp());

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

ライブラリ付属のwsdl2javaコマンドを使います。jarまで自動で作成してくれます。

set JAVA_HOME=javahomeのパス

wsdl2java.bat -d partner -b jaxb.xml -exsh true -autoNameResolution -clientjar partner.jar partner.wsdl
wsdl2java.bat -d metadata -b jaxb.xml -exsh true -autoNameResolution -clientjar metadata.jar metadata.wsdl

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

<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の設定

コード内で指定となっています。

    Client client = ClientProxy.getClient(bp);

HTTPConduit conduit = (HTTPConduit)client.getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
policy.setProxyServer("sampleproxyhost");
policy.setProxyServerType(ProxyServerType.HTTP);
policy.setProxyServerPort(9999);

ProxyAuthorizationPolicy authpolicy = new ProxyAuthorizationPolicy();
authpolicy.setUserName("sampleproxyuser");
authpolicy.setPassword("samplepassword");
conduit.setProxyAuthorization(authpolicy);

conduit.setClient(policy);

エンドポイントの設定

BindingProviderを使うことで指定します。

    URL wsdlurl = Soap.class.getClassLoader().getResource("/partner.wsdl");
Soap soapBinding = new SforceService(wsdlurl).getSoap();

BindingProvider bp = (BindingProvider)soapBinding;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,"https://login.salesforce.com/services/Soap/u/38.0");

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

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

セッションIDは、SOAPHeaderにセットしておくのではなく、APIを呼び出す際に引数で指定します。

        bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,loginResult.getServerUrl());

SessionHeader sh = new SessionHeader();
String sessionId = loginResult.getSessionId();
sh.setSessionId(sessionId);

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

cxf3.Xを使うとデータバインディングがとても遅かったので、cxf2.7がお勧めです。

Java: wsimportでSalesforce APIを使用する

まずはサンプルコード

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.MessageContext;

import com.sforce.soap.partner.LoginResult;
import com.sforce.soap.partner.ObjectFactory;
import com.sforce.soap.partner.SessionHeader;
import com.sforce.soap.partner.SforceService;
import com.sforce.soap.partner.Soap;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.Headers;
import com.sun.xml.ws.developer.WSBindingProvider;    
    
-------------------------------------------------------------------------
    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ではかなり改善しているように思います。

Java: axis2でSalesforce APIを使用する

まずはサンプルコード

    String url = "https://login.salesforce.com/services/Soap/u/39.0";   
SforceServiceStub soapBinding = new SforceServiceStub(url);


Options options = soapBinding._getServiceClient().getOptions();
options.setProperty(HTTPConstants.MC_ACCEPT_GZIP, Boolean.TRUE);
options.setProperty(HTTPConstants.MC_GZIP_REQUEST, Boolean.TRUE);

//httpプロキシを指定する
{
HttpTransportProperties.ProxyProperties proxyProperties = new HttpTransportProperties.ProxyProperties();
proxyProperties.setProxyName("samplehost");
proxyProperties.setProxyPort(9999);
proxyProperties.setUserName("sampleproxyuser");
proxyProperties.setPassWord("sampleproxypasword");
options.setProperty(HTTPConstants.PROXY, proxyProperties);
}


LoginDocument logininfo = LoginDocument.Factory.newInstance();
Login login = Login.Factory.newInstance();
login.setUsername("sampleuser@sample.username");
login.setPassword("samplepassword");
logininfo.setLogin(login);


LoginResult loginResult = soapBinding.login(logininfo, null,null).getLoginResponse().getResult();

options.setTo(new EndpointReference(loginResult.getServerUrl()));

SessionHeaderDocument sh = SessionHeaderDocument.Factory.newInstance();

SessionHeader sheader = SessionHeaderDocument.SessionHeader.Factory.newInstance();
sheader.setSessionId(loginResult.getSessionId());
sh.setSessionHeader(sheader);

GetServerTimestampDocument getServerTimestampDocument = GetServerTimestampDocument.Factory.newInstance();
getServerTimestampDocument.setGetServerTimestamp(GetServerTimestamp.Factory.newInstance());
System.out.println(soapBinding.getServerTimestamp(getServerTimestampDocument, sh, null).getGetServerTimestampResponse().getResult().getTimestamp());

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

apache2のサイト(Apache Axis2 – Apache Axis2/Java - Next Generation Web Services

)からライブラリをダウンロード

ダウンロードしたaxis2のライブラリにクラスパスを指定しコマンドを実行する。

set classpath=.;..\lib\*

java -cp %classpath% org.apache.axis2.wsdl.WSDL2Java -Euwc -Ejavaversion 1.5 -d xmlbeans -uri partner.wsdl
java -cp %classpath% org.apache.axis2.wsdl.WSDL2Java -Euwc -Ejavaversion 1.5 -d xmlbeans -uri metadata.wsdl

コマンドを実行するとJavaコードと共にresourcesのフォルダにxsbが出力されます。

resourcesフォルダのファイルは実行時に必要なので、Javaコードをコンパイルした後、コンパイルしたクラスとresourcesフォルダのファイルを含めてライブラリを作成します。

httpproxyの設定

httpproxyはプログラム内部の指定となります。

          HttpTransportProperties.ProxyProperties proxyProperties = new HttpTransportProperties.ProxyProperties();
proxyProperties.setProxyName("samplehost");
proxyProperties.setProxyPort(9999);
proxyProperties.setUserName("sampleproxyuser");
proxyProperties.setPassWord("sampleproxypasword");
options.setProperty(HTTPConstants.PROXY, proxyProperties);

エンドポイントの設定

コンストラクタで受けてくれるタイプです。

    String url = "https://login.salesforce.com/services/Soap/u/39.0";    
SforceServiceStub soapBinding = new SforceServiceStub(url);

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

optionsで、エンドポイントを指定しますが、SoapHeaderにセッションヘッダをセットはしません。セッションヘッダは実行時に毎回引数で指定することになります。

        options.setTo(new EndpointReference(loginResult.getServerUrl()));

SessionHeaderDocument sh = SessionHeaderDocument.Factory.newInstance();

SessionHeader sheader = SessionHeaderDocument.SessionHeader.Factory.newInstance();
sheader.setSessionId(loginResult.getSessionId());
sh.setSessionHeader(sheader);

 

これで上記コードのsoapBindingはSOAP APIを使うことができます。サンプルコードの最後に、Salesforceのサーバ時刻を取得するAPIをコールしています。

サーバ時刻を取得するAPIをコールするだけで、このコード量ですから、アプリケーションで書く場合のコード量はとても多くなります。

Java: axis1.4でSalesforce APIを使用する

まずはサンプルコード

    SoapBindingStub soapBinding = (SoapBindingStub)new SforceServiceLocator().getSoap();

soapBinding._setProperty(org.apache.axis.transport.http.HTTPConstants.MC_ACCEPT_GZIP , "true");
soapBinding._setProperty(org.apache.axis.transport.http.HTTPConstants.MC_GZIP_REQUEST , "true");

soapBinding._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, "https://login.salesforce.com/services/Soap/u/39.0");

//httpプロキシを指定する
{
Properties systemprops = System.getProperties();
systemprops.setProperty("http.useProxy", "true");
systemprops.setProperty("http.proxyHost", "samplehost");
systemprops.setProperty("http.proxyPort", "9999");
systemprops.setProperty("http.proxyUser", "sampleproxyuser");
systemprops.setProperty("http.proxyPassword", "sampleproxypasword");
}


LoginResult loginResult = soapBinding.login("sampleuser@sample.username", "samplepassword");

String serverUrl = loginResult.getServerUrl();

soapBinding._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, serverUrl);

SessionHeader sh = new SessionHeader();
sh.setSessionId(loginResult.getSessionId());
soapBinding.setHeader((new SforceServiceLocator()).getServiceName().getNamespaceURI(), "SessionHeader", sh);

System.out.println(soapBinding.getServerTimestamp().getTimestamp());

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

axis1.4のライブラリとcommonshttp3.1のライブラリを手に入れます。

ダウンロードサイトが見つからない場合はこちらにアップロードしました。(いいのかなこれ、何か問題ありましたら教えてください。)

 

axis1.4のライブラリへクラスパスを指定し次のコマンドでコードをジェネレートします。

set classpath=..lib*
java -cp %classpath% org.apache.axis.wsdl.WSDL2Java -a partner.wsdl

出力されたファイルをコンパイルし、実行時にaxis1.4のライブラリと共にクラスパスに含めます。

httpproxyの設定

通信時にhttpproxyを使う場合は、次のようにシステムプロパティをセットします。システムプロパティなので、javaコマンドの引数からも指定可能です。

      Properties systemprops = System.getProperties();
systemprops.setProperty("http.useProxy", "true");
systemprops.setProperty("http.proxyHost", "samplehost");
systemprops.setProperty("http.proxyPort", "9999");
systemprops.setProperty("http.proxyUser", "sampleproxyuser");
systemprops.setProperty("http.proxyPassword", "sampleproxypasword");

エンドポイントの設定

本番やSandBoxのapiのエンドポイントを指定します。何も指定しないと、wsdlに記載のものを使います。

        soapBinding._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, "https://login.salesforce.com/services/Soap/u/39.0");

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

login後に、セッションIDとインスタンスURLが返却されます。インスタンスURLでエンドポイントを再設定し、セッションIDをSOAP Headerにセットします。

        String serverUrl = loginResult.getServerUrl();
soapBinding._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, serverUrl);

SessionHeader sh = new SessionHeader();
sh.setSessionId(loginResult.getSessionId());
soapBinding.setHeader((new SforceServiceLocator()).getServiceName().getNamespaceURI(), "SessionHeader", sh);

transportにhttpcommons3を使う

axis1.4の標準のtransportはaccept-Encodingでgzipに対応していないので、httpcommons3を使うように変更します。

client-config.wsddファイルに次の記載をします。

<?xml version="1.0" encoding="UTF-8"?>
<deployment name="defaultClientConfig"
xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<globalConfiguration>
<parameter name="disablePrettyXML" value="true"/>
<parameter name="enableNamespacePrefixOptimization" value="false"/>
</globalConfiguration>
<transport name="http" pivot="java:org.apache.axis.transport.http.CommonsHTTPSender"/>
<transport name="https" pivot="java:org.apache.axis.transport.http.CommonsHTTPSender"/>
<transport name="local" pivot="java:org.apache.axis.transport.local.LocalSender"/>
<transport name="java" pivot="java:org.apache.axis.transport.java.JavaSender"/>
</deployment>

そして、実行時のjavaコマンドのシステムプロパティで、

-Daxis.ClientConfigFile=client-config.wsdd

を指定します。

 

以上を行うことで、上記コードのsoapBindingは、Salesforce APIとやりとりができる状態になります。

Java: 色々なJavaライブラリでSalesforceAPIを使う

SOAP APIJavaクライアントには、色々なライブラリがあります。それぞれのJavaライブラリでSalesforce APIを使ってみた結果をまとめてみます。

サマリ

Java ライブラリ SOAP API Metadata API xsd:intにバインドされる型 XML中の不正な制御コード データバインディングの性能
Axis1.4 × Integer エラー 普通
Axis2 adb × × Integer エラー
xmlbeans int(対応するboolean変数で値がセットされたかどうかは判定可能) エラー やや早い
wsimport Integer エラー 普通
apache cxf Integer やや遅い
wsc int(対応するbooleanで値がセットされたかどうか判定可能だが、変数がprivateであり値を取得するためにリフレクションが必要) 除去する やや早い

 

比較軸について

  • SOAP API、METADATA APIが使えるかどうかにしました。”○”が対応していて、”×”が対応していません。
    また、SOAP APIはパートナーWSDLを対象としています。
  • xsd:intをJavaコードにした際にバインドされるクラスを書きました。理由は、xsd:int の値でSalesforceからnullが返却される時があるからです。
    Java側でIntegerではなくintでうけると、nullは0となります。nullに意味がある場合は注意が必要となります。
  • データバインディングの性能を記載しました。300項目、300行を返却するSOQLをqueryし、結果を取得するまでかかった時間で測定しています。各ライブラリで計測した時間は次の通りでした。apache cxfがやや遅いです。
Java ライブラリ 1回目(ms) 2回目(ms) 3回目(ms) 平均(ms)
axis1.4 5726 5196 5165 5362.33
aixs2(xmlbeans) 5054 4212 4571 4612.33
wsimport 5928 6037 5819 5928
apache cxf 7572 7894 7474 7646.67
wsc 4181 4508 4358 4349
実行環境:
java : jdk8 32bit u121
axis1.4 : axis1.4_7 , commons-httpclient-3.1
wsimport : version 2.2.9 , JAXWS2.2.7
apache cxf : 2.7.18
wsc : force-wsc-27.0.0.jar , gson-2.8.0.jar , rhino-1.7.7.1.jar

 

   Axis1.4

  • 古いけど、使いやすいです。(特にSObject型が扱い易いです。queryの結果や、insert/upsert/updateでSObjectを使う際に扱い易さが実感できます)
  • 残念なことにMetadataAPIをJavaクラスに変換するとエラーが出るようになってしまいました。(APIVer32までは問題なく変換できていました)。
  • 「Accept-Encoding: gzip 」を使うためには、commons http3.Xが必要です。Commons http3.Xをclasspathに含めて、client-config.wsddファイルで、transportをCommonsHTTPSenderに変更します。

   Axis2

  • Axis2には、JavaClientを作る方法が複数あって、adbとxmlbeansを記載しました。(他にもjaxbriというのがありましたが、SOAP APIのコードジェネレータでエラーが発生してしまい解決できませんでした。)
  • adbはwsdlのextensionが対応してなくて両方×でした。
  • Axis2のxmlbeansは、コードジェネレータでjavaコードとxsbという拡張子のバイナリファイルが出力されます。そのxsbファイルも実行時に必要となります。
  • Axis2のxmlbeansでコードを生成すると、細かく制御ができるのですが、コード量が他に比べて圧倒的に多くなります。

   wsimport

  • Jdkに標準ではいっているコードジェネレータコマンド
  • Webサービスコールの初回で、wsdlファイルを探しに行ったり、標準のままではエンドポイントを変更できなかったりします。
  • 上記のようにwsdlを探しにいくので、salesforceの場合は、jarにwsdlを含めておく必要があります。
  • 標準のままではエンドポイントを変更できないので、 (https://mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt)から追加のライブラリを入手が必要です。

   apache cxf

  • jaxbやxmlbeansがデータバインディングに使えます。
  • 細かくオプションが用意されていて使いやすい(んだと思います)。
  • wsimportと同様にwsdlを探しにいくので、salesforceの場合は、jarにwsdlを含めておく必要があります。
  • apache cxf2.7とapache cxf3.1を比較すると、cxf3.1の方がデータバインディングがかなり遅くなるので、cxf2.7がおすすめです。

   wsc (Force.com Web Services Connector)

  • salesforceのWebServive用のコードジェネレータ、現時点ではスタンダードなライブラリで、Salesforceのヘルプサイトのサンプルコードもこのライブラリで書かれています。
  • 色々な所で工夫されていてコード量が少なくてすみます。
    例えば、login後に、エンドポイントの変更や、セッションIDのセットを自動で行ってくれたりします。
  • データの登録(insert,upsert,update)の際に、partner.wsdlであってもクライアント側で項目の型とセットする型をチェックしてくれます。項目の型とセットする型は次のチェックです。
    日付型:java.util.Calendar
    日付/時間型:java.util.Calendar
    チェックボックス型:java.lang.Boolean
    int型:java.lang.Integer
    base64型:byte[]
  • xsd:intがJavaのプリミティブintにバインドされますが、nullだったかどうかが通常では判断できません。(private変数に{フィールド名}__is_setがあり、値がセットされたかは判断できるので、リフレクションを使うことで判断はできますが、無理矢理なやり方だと思います)

次回からそれぞれについて、細かくコードを紹介したいと思います。

 

補足

各ライブラリでのSalesforceへレコードを作成・更新する際のSobjectへの項目値の指定の仕方は次の記事に書きました。

crmprogrammer38.hatenablog.com