この 1 年間で、クラウド・コンピューティングは多種多様なアプリケーション (Salesforce CRM や Google Apps など) とサービス (Amazon EC2 (Amazon Elastic Compute Cloud) でホストされた IBM® DB2®、Google App Engine、Salesforce による Force.com プラットフォームなど) を組み込むまでに急成長しました。企業は、これらのサービスが提供する完全なプラットフォームをベースに IT アプリケーションを構築してホストするため、こうしたサービスはよく PaaS (Platform-as-a-Service) と呼ばれます。
PaaS オファリングは、ハードウェアおよびソフトウェア・インフラストラクチャーが共有されるマルチテナント環境でホストされます。マルチテナント環境は、それぞれの組織のデータがセキュアな方法で他の組織から確実に隔離されるようにセットアップされます。これは、オフィス空間を買うのではなく借りるのと同じようなものであり、保守やアップグレードについて心配する必要はありません。そのため組織は PaaS によって多大なコスト削減を実現できるだけでなく、アップグレードは自動で行われ、保守も不要であるといった特有のメリットも手に入れることができます。新しい IT プロジェクトには予算を超過したり、期限に間に合わなかったりするというリスクが付き物ですが、このモデルには、そのようなリスクを減らすという独特のメリットもあります。
Salesforce は PaaS を提供するプロバイダーの 1 つです。Salesforce はホスト型 CRM (ホスト型顧客関係管理) ソリューションの提供に乗り出し、このオファリングによって販売、パートナー関係管理、マーケティングなどの一般的なビジネス・アプリケーションにそのまますぐに利用できる、いわゆるターンキー・ソリューションが実現しました。Salesforce がこのプラットフォームをベースに開始した Force.com は、ブラウザーまたは Eclipse べースの Force.com 統合開発環境 (IDE) でカスタムのビジネス・アプリケーションを構築できる完全な PaaS です。さらに Force.com では、Apex という独自の Java™ 風プログラミング言語を使用してアプリケーションをカスタマイズすることもできます。
Salesforce では、言語にもプラットフォームにも依存しないという利点を持つ SOAP を使用して、ユーザーがサーバーを操作できるようにしています。サーバーで使用可能な操作を記述するには、WSDL (Web Services Description Language) を使用します。WSDL 文書は、ポートと呼ばれる一連のネットワーク・エンドポイントを記述してから、サーバーと交換するメッセージまたはデータの XML フォーマットを定義します。
この記事のサンプルでは、JAX-WS (Java API for XML Web Services) を使用します。JAX-WS が XML および SOAP の操作を簡易化するために提供している多数のツールのなかには、WSDL 文書から必要なドメイン・オブジェクトを自動的に生成するツール、そして XML を Java オブジェクトに自動的にバインドするツールもあります。
Salesforce とエンタープライズ・アプリケーションとの統合
Salesforce との統合アプリケーションを構築するには、いくつもの方法があります。まず 1 つは、レコードが作成または更新されるたびに Web サービスを使用してメッセージを送信するワークフローを Salesforce で構成するという方法です。このプロセスではアウトバウンド・メッセージとして WSDL を使用します。これは、データベースにトリガーを構成するのと同じようなプロセスです。ワークフローは、データが作成、更新、または削除されるたびに、設定済みの URL の宛先に SOAP メッセージを送信するように Salesforce を構成します。例えば、この記事で使用するマイレージの例では、新しいレポートが追加されると、マイレージ・レポートが内部システムに送信されるようにすることが可能です。
2 つ目の方法は、SOAP Web サービスを使用して Salesforce と直接やりとりする機能を提供します。この方法では、Salesforce が生成する WSDL ファイルをカスタム Web サービスで使用します。このタイプの統合のために、Salesforce では以下の 2 つのタイプの WSDL を用意しています。
- パートナー WSDL。弱い型付けであるため、複数の組織で使用することができます。ただし、パートナー WSDL を扱うのは、もう一方のタイプほど簡単ではありません。それは、XML を組織のオブジェクト表現として適切になるようにマーシャリングしなければならないためです。
- エンタープライズ WSDL。強い型付けを持つこのタイプの WSDL は、単一の組織にバインドされます。そのため扱いやすくはなりますが、1 つの組織のスキーマでしか機能しません。また、オブジェクトを変更するには WSDL を再び生成しなければならなくなります。
この記事では、Force.com の Workbook で例として使用されているマイレージ追跡アプリケーション (このチュートリアルのリンクについては、「参考文献」を参照) を拡張する手順を通して、Salesforce との統合方法を説明します。サンプルでは、上記の WSDL のうち、エンタープライズ Web Services WSDL を使用します。
Salesforce Web サービスを使用するにはセキュリティー・トークンが必要です。Salesforce UI を使用してセキュリティー・トークンを受け取るか、あるいはリセットしてください。リセットするには、Setup > My Personal Information > Reset your security token の順にクリックします。すると、セキュリティー・トークンをリセットするオプションが表示されるので、このオプションによってセキュリティー・トークンを自分の E メール・アドレスに送信させます。受け取ったセキュリティー・トークンは、ログイン・パスワードと組み合わせて使用してください。例えばパスワードが aaaaaa で、セキュリティー・トークンが XXXXXXXXXX であれば、パスワードの代わりに aaaaaaXXXXXXXXXX と入力します。
Web サービスを利用することによる Salesforce との統合
XML を使用して Salesforce を統合する方法を説明する例として、Salesforce にログインし、Salesforce のデータに対してクエリーを実行して新しいレコードを作成するサンプル Java アプリケーションを開発しました。Salesforce との通信は、一貫してセキュアな HTTPS チャネルで行われます。Salesforce を統合する Java Web サービスをセットアップするには、以下の手順に従ってください。
- Salesforce でエンタープライズ WSDL 文書を生成します。
組織の Salesforce アカウントにログインし、Setup > Develop > API の順にクリックします。次に、生成したい WSDL 文書を右クリックします。
- WSDL から Java クラスとドメイン・オブジェクトを生成します。
このサンプルに使用したのは、JAX-WS ツールキットに付属の wsimport ツールです。wsimport の実行プロセスを簡単にするため、このサンプルでは Salesforce サンプルに含まれているビルド・テンプレートを使用します。このツールが生成する Java ドメイン・オブジェクトによって、XML 文書が自動的に Java オブジェクトにバインドされるようになるため、サーバーとの通信が簡単に行えるようになります。
- Web サービスから Salesforce にログインします。
サーバーの URL とセッション ID を取得するには、まずログインする必要があります。リスト 1 のコードに、ログイン方法を示します。
ログインで重要な部分は、サーバー URL の取得です。Salesforce は na1.salesforce.com や na2.salesforce.com などの複数のインスタンスで稼働することによって、信頼性とパフォーマンスを向上させます。いったんサーバーにログインしたら、セッション情報を保持するために以降の Web サービス呼び出しにはすべて同じ URL を使用してください。
リスト 1. Salesforce へのログイン
public void doLogin(String userName, String password) {
if (userName.length() == 0 || password.length() == 0) {
throw new RuntimeException("user name length and/or password length
cannot be 0 length. \n",
new IllegalArgumentException("Invalid password or user name\n"));
} else {
try {
URL wsdlLocation =
this.getClass().getClassLoader().
getResource("etc/enterprise.wsdl");
if (wsdlLocation == null) {
WebServiceException e =
new WebServiceException("enterprise.wsdl not found!");
//exceptionLogger(e.getMessage(), e);
throw e;
}
port = new SforceService(wsdlLocation,
new QName("urn:enterprise.soap.sforce.com",
"SforceService")).getSoap();
} catch (WebServiceException wse) {
//exceptionLogger("Error creating salesface port ", wse);
throw wse;
}
try {
loginResponse = port.login(userName, password);
} catch (Exception e) {
System.out.println("Error logging in to Salesforce.com " + e);
return;
}
System.out.println("Login was successful.");
System.out.print("The returned session id is: ");
System.out.println(loginResponse.getSessionId());
System.out.print("Your logged in user id is: ");
System.out.println(loginResponse.getUserId() + " \n\n");
System.out.print("The server url is: ");
System.out.println(loginResponse.getServerUrl() + " \n\n");
// on a successful login, you should always set up your session id
// and the url for subsequent calls
....
|
クライアント・セッションはこれでセットアップできたので、Web サービス呼び出しを介してサーバーを呼び出し、サーバーとやりとりすることができます。この記事ではこれから、クエリーによってサーバーからデータを読み出す操作、そして新規レコードをサーバーに追加する操作を説明します。
Web サービスを利用することによる Salesforce へのクエリー
Salesforce のデータに対してクエリーを実行するには、SOQL (Salesforce Object Query Language) という特殊な言語を使う必要があります。SOQL クエリーでは、SELECT を使ってテーブル内の特定のフィールドに対してクエリーを実行するのと似たような方法で、特定のオブジェクトやオブジェクトの特定のフィールドを検索することができます。さらに、SOQL クエリーを使用して検索基準を満たすレコード数をカウントしたり、検索結果を特定の順序でソートしたりすることもできます。
SOQL の操作をマスターする最短の方法は、Force.com IDE の Schema Explorer を使用することです (図 1 を参照)。プロジェクト内で Schema Explorer を開くには、プロジェクトのルートにある salesforce.schema をダブルクリックします。Schema Explorer内では、組織のすべてのオブジェクトを参照することができます。そして各種のオブジェクトをドリルダウンすることで、個々のオブジェクトやフィールドを選択して自動的にクエリーを生成することができます。また、オブジェクトの子のリストをドリルダウンして、関連オブジェクトをクエリーに含めることも可能です。
図 1. Schema Explorer
実行する必要がある SOQL クエリーを完成させたら、そのクエリーを Web サービス呼び出しに組み込みます。このクエリーによって返される結果は、データの XML 表現です。この XML 文書は、前の手順で JAX-WS によって生成した Java ドメイン・オブジェクトに自動的にバインドされます。
サンプル・アプリケーションではまず、単純にすべてのマイレージ・レコードを取得します。クエリーの結果は、セットアップに生成されたドメイン・オブジェクトにバインドされます (リスト 2 を参照)。
リスト 2. マイレージ検索のクエリーの結果
public void getMileageReports(ForceLogin login) throws UnexpectedErrorFault,
InvalidSObjectFault, InvalidIdFault, InvalidQueryLocatorFault,
MalformedQueryFault, InvalidFieldFault {
QueryResult queryResult = login.port
.query("Select Contact__c, Date__c, Miles__c from Mileage__c");
if (queryResult.getSize() > 0) {
List<SObject> records = queryResult.getRecords();
for (SObject record : records) {
MileageC mileageC = (MileageC) record;
System.out.println(mileageC.getMilesC().getValue()
+ " " + mileageC.getContactC().getValue());
}
}
}
|
これらのクエリーを作成する際に注意しなければならない重要な点は、カスタムのオブジェクトおよびフィールドを標準のオブジェクトおよびフィールドとは区別することです。カスタム・オブジェクトとは、それぞれの組織に固有の Salesforce データベース・テーブルのことです。カスタムのオブジェクトおよびフィールドを、同じ名前を持つ標準の Salesforce オブジェクトとは別のものとして確実に区別するには、オブジェクトの名前の末尾に __c を追加してください。これによって、標準のオブジェクトおよびフィールドとは区別されます。
リスト 2 のクエリーは、Mileage オブジェクトからすべてのレコードを取得します。__c を付ける規約によって、Mileage オブジェクトのカスタム・フィールドの名前であるということが明確にされている点に注目してください。これで、クエリーの結果を繰り返し処理し、エンタープライズ WSDL から生成されたドメイン・オブジェクトである MileageC オブジェクトに結果をバインドすることができます。すると MileageC オブジェクトには、マイレージ・レコードに対応する実際のデータが自動的に設定されます。
前のクエリーはすべてのマイレージ・レコードを返しましたが、大抵の場合、それでは必要なレコード以外のレコードも含まれてしまいます。幸い、特定のレコードだけを取得するためにフィルター基準 (日付や連絡先など) を指定することが可能です。例えば、マイル数が 300 を超えているすべてのマイレージ・レポートを検索したいとします。その場合には、クエリーを以下のように変更します。
Select Name, Miles__c From Mileage__c where Miles__c > 300 |
データをフィルタリングする方法としては、特定の期間中に変更されたデータだけを取得するという方法もあります。それには、クエリーを実行する代わりに getUpdated メソッドを使用します。このメソッドで引数に取るのは、更新を検索する対象のオブジェクトと、開始日、終了日です。すると、getUpdated は指定された期間中に変更されたすべてのオブジェクトを返します。リスト 3 のコードは、この 1 ヶ月間に変更されたすべてのマイレージ・レコードを返します。
リスト 3. 更新されたマイレージ・レコードの取得
GregorianCalendar cal =
port.getServerTimestamp().getTimestamp().
toGregorianCalendar();
GregorianCalendar calEnd = (GregorianCalendar) cal.clone();
cal.add(GregorianCalendar.MONTH, -1);
DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
GetUpdatedResult updatedRecords = port.getUpdated("Mileage",
datatypeFactory.newXMLGregorianCalendar(cal),
datatypeFactory.newXMLGregorianCalendar(calEnd));
|
getUpdated と同様の操作には、getDeleted もあります。同じメソッド・シグニチャーを持つこのメソッドは、特定の期間中に削除されたすべてのレコードを返します。
Salesforce に対してクエリーを実行するもう 1 つの方法は、SOSL (Salesforce Object Search Language) を使用することです。SOSL はテキスト・ベースの検索に似ていますが、それよりも自由な形式の検索を行えます。SOSL では、どのオブジェクトにデータが含まれているかわからないとしても、関連のない複数のオブジェクトを検索対象にすることができます。SOSL を使用して実行するクエリーでは、関連のない複数のオブジェクトを返すことも可能です。
この柔軟性を示す絶好の例が、リスト 4 のコード (Salesforce JAX-WS サンプルから引用した SOSL クエリーの例) です。顧客 (contacts)、見込み客 (leads)、アカウント (accounts) に対して電話番号を問い合わせるこのコードから、関連のない複数のオブジェクトで検索を行う方法がわかります。
リスト 4. 複数のオブジェクトでの電話番号の検索
SearchResult searchResult = port
.search("find {4159017000} in phone fields returning
contact(id, phone, firstname, lastname),
lead(id, phone, firstname, lastname),
account(id, phone, name)");
List<SearchRecord> records = searchResult.getSearchRecords();
List<Contact> contacts = new ArrayList<Contact>();
List<Lead> leads = new ArrayList<Lead>();
List<Account> accounts = new ArrayList<Account>();
if (records != null && !records.isEmpty()) {
for (SearchRecord recordType : records) {
SObject record = recordType.getRecord();
if (record instanceof Contact) {
contacts.add((Contact) record);
} else if (record instanceof Lead) {
leads.add((Lead) record);
} else if (record instanceof Account) {
accounts.add((Account) record);
}
}
}
|
上記の例では、query ではなく search メソッドを使って SOSL クエリーを実行しています。結果が SearchResults オブジェクトにマッピングされると検索結果の個々のレコードが検査されて、それぞれのタイプが判断されます。
Web サービスによる Salesforce へのデータの追加
Salesforce にデータを追加するのにも、エンタープライズ WSDL から生成された同じドメイン・オブジェクトを使用します。これらのオブジェクトには通常のオブジェクトと同じようにデータが設定された後、XML にシリアライズされ、永続化のためにサーバーに渡されます。ここで課題となるのが、異なるオブジェクト間の関係の作成です。例えば、特定の連絡先に対して新しいマイレージ・レコードを追加したいとします。その場合、まずは対象とする連絡先を検索しなければなりません。この例では、名前 (FirstName) が Edna という人の連絡先を検索します (リスト 5 を参照)。
リスト 5. データベース内の特定の連絡先の検索
QueryResult qr = login.port
.query("Select Id, FirstName, LastName, AccountId " +
"from Contact where FirstName = 'Edna'");
String contactID = null;
if (qr.getSize() > 0) {
Contact contact = (Contact) qr.getRecords().get(0);
contactID = contact.getId().getValue();
}
|
このクエリーは、リストの最初にある連絡先の ID を返します (実際のアプリケーションでは、正しいレコードであることを確実にするために、さらに広範なエラー・チェックを行う必要があります)。ユーザーの ID を受け取ったら、その ID を新しいマイレージ・レコードに保管します (リスト 6 を参照)。
リスト 6. 新規マイレージ・レコードの作成
MileageC mileageC = new MileageC();
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(new Date());
XMLGregorianCalendar activityDate =
DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
mileageC.setDateC(soFactory.createMileageCDateC(activityDate));
mileageC.setMilesC(soFactory.createMileageCMilesC(new Double(300)));
mileageC.setContactC(soFactory.createMileageCContactC(contactID));
|
これでマイレージ・レコードはセットアップできたので、今度は Web サービスで create メソッドを呼び出して、Salesforce にレコードを永続化します。create はオブジェクトのリストを引数に取ることに注意してください。これは、複数のマイレージ・レコードを同時に追加できるようにするためです。リスト 7 に、データを Salesforce に永続化するためのコードを記載します。
リスト 7. データベースへの新規マイレージ・レコードの保管
// call the create method passing the array of tasks as Sobjects
ArrayList list = new ArrayList();
list.add(mileageC);
List<SaveResult> saveResults = login.port.create(list);
for (SaveResult saveResult : saveResults) {
if (saveResult.isSuccess()) {
System.out.println("saveResult success, id= " + saveResult.getId());
} else {
System.out.println("saveResult error");
// there were errors during the create call, go through the
// errors and write them to the screen
List<Error> errorList = saveResult.getErrors();
for (Error error : errorList) {
System.out.println("Error code is: "
+ error.getStatusCode().toString());
System.out
.println("Error message: " + error.getMessage() + "\n\n");
}
}
}
|
上記のコードはレコードを Salesforce に永続化して、レコードの保管が成功したか、失敗したかを示す保管結果のリストを返します。Salesforce から返されるエラー・メッセージは傾向としてかなり説明的なので、何が上手く行かなかったのかが容易にわかります。ただし、この極めて単純な手法では、必ずデータが Salesforce に正しく保管されるようにすることまではできません。重要なデータの場合には、データが正常に保管されるまで、自動的に保管操作が再試行されるようにすることを検討してください。
同じような方法で Salesforce のデータを更新することもできます。その最も簡単な方法としては、まず変更するレコードに対してクエリーを実行し、それからそのレコードをオブジェクトにロードします。そしてオブジェクトを更新した後、update メソッドを呼び出します。
データを更新するもう 1 つの方法は、重複するレコードをマージする方法です。マージ操作では、マスター・レコードとマージするレコードとを指定します。このようにして、マスター・レコードをマージ・レコードのすべてのデータで更新します。一度に最大 3 つのレコードをマージすることができます。
XML の力を利用することで、クラウドのデータを既存のエンタープライズ・アプリケーションに簡単に統合できるようになります。XML は、多種多様なサービスおよび言語の間で交換できる共通のデータ・フォーマットを提供するからです。この記事のサンプル・アプリケーションでは、具体的な手段として SOAP を使用して、XML Web サービスを介して Salesforce とやりとりする方法を説明しました。
Web サービスによってオンサイトのアプリケーションにクラウドのデータを統合するというこの手法は、Google Apps から Basecamp に至るまで、他のさまざまなアプリケーションでも使用することができます。クラウドのサービスを統合することにより、組織はクラウド内に各種の新しいアプリケーションを迅速に構築できると同時に、これまでのソフトウェアへの投資をそのまま活用することが可能になります。
学ぶために
- Force.com Fundamentals: Force.com とクラウドとの連携について詳しい情報を入手してください。
- The Force.com Workbook: Force.com のワークブックで、Force.com プラットフォームを使用して独自のオンデマンド・アプリケーションを構築する方法をさらに学んでください。
- JAX-WS: Java プラットフォームでの XML ベースのリモート・プロシージャー・コール (RPC) プロトコルをサポートする API および規約を定義する JAX-WS の詳細を学んでください。
- JAX-WS User Guide: JAX-WS を使用するために必要な知識を身につけてください。
- IBM XML 認定: XML や関連技術の IBM 認定技術者になる方法について調べてください。
- XML Technical library: 広範な技術に関する記事とヒント、チュートリアル、標準、そして IBM Redbooks については、developerWorks XML ゾーンを参照してください。
- developerWorks の Technical events and webcasts: これらのセッションで最新情報を入手してください。
- Technology bookstore: この記事で紹介した技術やその他の技術に関する本を参照してください。
- developerWorks podcasts: ソフトウェア開発者向けの興味深いインタービューとディスカッションを聞いてください。
製品や技術を入手するために
- Mileage ソース・コード: Force.com Workbook の Mileage チュートリアル・ソース・コードをダウンロードしてください。
- IBM 製品の評価版: DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® のアプリケーション開発ツールとミドルウェア製品を体験するには、IBM SOA Sandbox のオンライン・トライアルをダウンロードするか、検討してみてください。
議論するために
- XML ゾーンのディスカッション・フォーラム: XML 関連のフォーラムに参加してください。
- developerWorks blogs: developerWorks blogs から developerWorks コミュニティーに加わってください。
Ryan Knight は、12 年以上、最新技術を利用する企業を支援してきた経験を持つ Senior Technical Consultant です。これまで、Oracle や IBM、そして Williams Pipeline などの大企業および中小企業に協力し、クラウド・コンピューターからエンタープライズ・アーキテクチャーに至るまでの技術を利用してきました。Adobe Flex と Java 技術での彼の広範な経験については、anvilflex.com の彼のブログを読んでください。