Web サービスを統合した、移植可能な Java 旅行アプリを作成する

2014年 8月 07日
PDF (1226 KB)
 
Felicia Tucci

Felicia Tucci

IBM Application Architect

@FeliciaTucci

IBM Bluemix™ にサインアップ
無償のサービス、ランタイム、インフラを含むクラウド・プラットフォームが、新たなモバイルやウェブ・アプリのクイックな構築とデプロイを実現します。

クラウド内でアプリケーションを作成、デプロイすると実現できる、高いレベルの相互運用性と移植性をデモンストレーションするために、私は簡単で楽しい旅行アプリケーションを作成することにしました。

このアプリケーションは 2 つの部分で構成され、ユーザー・プロファイルに格納されたユーザーの希望に関する情報を活用して、ある地域でどんな宿泊施設を利用できるかを示す地図を表示します。第 1 の部分では、Bluemix で利用できる MongoDB サービスを使用してユーザーの希望に関する情報を扱い、その結果として生成されるサービスを API を通じて外部に公開します。第 2 の部分は第 1 の部分と Web アプリケーションの外部サービスとを統合します。

クラウド・アプリケーションは、スケーラブルかつ移植可能であるとともに、内部サービスとの統合が容易でなければなりません。また、ライフサイクル全体を通じて行われる、プロビジョニングや管理に手間がかかるようであってはなりません。

注:

  • 「アプリを実行する」をクリックすると、任意の ID とパスワードでログインすることができます。
  • この演習のためにコードをフォークするには、「コードを入手する」ボタンをクリックすると表示されるページで、右上隅にある「EDIT CODE (コードを編集)」ボタンをクリックし (まだログインしていない場合は、皆さんの DevOps Services 資格情報を入力します)、メニューにある「FORK (フォーク)」ボタンをクリックして新しいプロジェクトを作成します。あるいは、ルート・フォルダーを選択し、左ナビゲーションから「File (ファイル)」 > 「Export (エクスポート)」の順に選択することにより、コードをエクスポートすることもできます。

同様のアプリケーションを作成するために必要となるもの

 

ステップ 1. クラウド・アプリケーションを作成する

 

私はこのサンプル・アプリケーションを Bluemix 上で作成してデプロイしました。ここでは Java と Spring Framework を選択しました。アプリケーションを作成するには、Bluemix にアクセスしてアプリケーションのタイプ (Java スタンドアロン、Java Web、Ruby など) を選択するだけで十分です。今回の場合は Java Web を選択します。

Bluemix サイトを使用してアプリケーションを作成する画面のスクリーン・ショット

クリックして大きなイメージを見る

ステップ 2. cf コマンドライン・ツールをインストールして使用する

 

アプリケーションの管理には、Bluemix の Web インターフェース、または Cloud Foundry プロジェクトが提供するコマンドライン・インターフェースを使用することができます。このサンプル・アプリケーションでは cf コマンドライン・インターフェースを使用することにしました。このコマンドラインにより、アプリケーションのデプロイ、サービスとの関連付け、制御 (起動と停止) などを行うことができます。この CLI を GitHub からダウンロードして、インストーラーを起動します。インストールが終わると cf.exe という実行可能ファイルが存在するようになります。まず、ターゲットとなる API エンドポイントを設定し、それからログインする必要があります。

API エンドポイントにログインする画面のスクリーン・ショット

ログインすると、アプリケーション、サービス、バインドされたサービスを一覧表示することができます。

ステップ 3. 開発環境を準備する

 

このサンプル・アプリケーションでは MVC Spring フレームワークを使用します。使用した環境は Spring Tool Suite に Cloud Foundry プラグインを追加したものです。ここで使用しているツールと技術は以下のとおりです。

  1. Spring 3.1.1
  2. JDK 7
  3. Spring Tool Suite 3.4.0 と Cloud Foundry Integration for Eclipse 1.5.1

Cloud Foundry プラットフォーム上にデプロイされる Java Web プロジェクトを作成する場合、Cloud Foundry としての性質をプロジェクトに追加する必要があります。そのためには、Cloud Foundry ランタイムに対する、アプリケーションおよびアプリケーションのリソースからの要求を記述する manifest.yml ファイルを作成します。

ここでは、以下の 2 つの部分で構成されるアプリケーションを作成します。

  • 第 1 の部分は UserService です。UserService はユーザー情報を扱うための API を公開し、人気の高い NoSQL データベースである MongoDB を内部クラウド・プラットフォームとして使用して、データの永続化を実現します。
  • 第 2 の部分は MyVacations です。MyVacations によって、ログインしたユーザーはパラメーターを指定して、利用可能なホテルを検索することができます。これらの検索パラメーターの一部の値は、UserService アプリケーションによって提供されます。ホテルのリストや、ホテルの情報は、Expedia のサービスによって提供されます。Google Maps API ウェブサービスは、リストに記載されているホテルの位置を地図上に表示します。
この例で説明されているアプリケーションのデプロイメント・モデルを単純化した図

UserService は、ユーザーに関する情報を MongoDB サービスを使用して中央に格納する方法を示します。

UserService の機能は以下のとおりです。

  1. ロギング機能 — UserService はユーザー名とパスワードを受信し、そのユーザーをデータベースで検索します。そのユーザーが見つかると、UserService はそのユーザーを返し、見つからない場合は新しいレコードを作成します。
  2. プロファイル検索機能 — UserService はユーザー名を受信し、そのユーザーの情報を検索し (希望の場所、大人の人数、子供の人数)、それらの情報をクライアントに返します。

ステップ 4. クラウド・サービス (MongoDB) をバインドする

 

MongoDB サービスを使用するには、始めにこのサービスのインスタンスを作成する必要があります。

  1. Bluemix に接続し、「DASHBOARD (ダッシュボード)」ビューで「Add a Service (サービスを追加)」を選択します。 Bluemix のダッシュボードに表示されたアプリケーション

    クリックして大きなイメージを見る

  2. 利用可能なサービスの一覧から MongoDB を選択し、このサービスのインスタンスを作成します。 UserService アプリケーションに対する MongoDB サービスのインスタンスを作成する

    クリックして大きなイメージを見る

あるいは、コマンドラインを使用して以下のコマンドを実行することもできます。

cf create-service mongodb 100 mongodb_ser1
(USAGE:    cf create-service SERVICE PLAN SERVICE_INSTANCE)

cf bind-service UserService mongodb_ser1
(USAGE:    cf bind-service APP SERVICE_INSTANCE)

これで MongoDB サービスのインスタンスが作成され、UserService にバインドされました。

ステップ 5. アプリケーションの中で MongoDB サービスを使用する

 

MongoDB サービスを作成してアプリケーションに関連付けると、このサービスの構成は読み取り専用の環境変数 VCAP_SERVICES に追加されます。この変数にはさまざまな情報が含まれ、これらの情報をコードに使用してサービスに接続することができます。現在、コードには以下のような情報が含まれています。

{
  "mongodb-2.2": [
      {
         "name": "mongodb-ser1",
         "label": "mongodb-2.2",
         "plan": "100",
         "credentials": {
            "hostname": "10.0.116.106",
            "host": "10.0.116.106",
            "port": 10192,
            "username": "46c538a6-e6e1-4d02-8132-77b0a4b2dc1c",
            "password": "0ceea0ea-5548-46ad-9b09-1002683aeca7",
            "name": "946dc87b-b455-4d12-b977-1b1ee22f1ade",
            "db": "db",
            "url": "mongodb://46c538a6-e6e1-4d02-8132-77b0a4b2dc1c:
0ceea0ea-5548-46ad-9b09-1002683aeca7@10.0.116.106:10192/db"
         }
      }
   ]
}

VCAP_SERVICES 変数を使用して MongoDB サービスのインスタンスに接続するには、JsonNode“url” を抽出します。この URL の中に、データベースに接続するためのすべてのパラメーター (ユーザーのクレデンシャル、ホスト名、ポート、データベースの名前) が含まれていることに注意してください。

private static String getUrlConnection() {
			
	String env = System.getenv("VCAP_SERVICES");
	ObjectMapper mapper = new ObjectMapper();
	try {
		JsonNode node = mapper.readTree(env);
		Iterator<JsonNode> dbNode = node.get("mongodb-2.2").getElements();
			
		JsonNode cred =  (JsonNode)dbNode.next().get("credentials");
			
		String uri =cred.get("url").getTextValue();
			
		logger.debug ("url db: " + uri);
			
		return uri;
			
	} catch (JsonGenerationException e) {
		logger.debug(e.getMessage());
	} catch (JsonMappingException e) {
		logger.debug (e.getMessage());
	} catch (IOException e) {
		logger.debug (e.getMessage());
	}
			
	return null;
}

DB 接続を作成するために MongoDB Java ドライバーを使用して Spring 構成クラスを作成し、DB オブジェクトをクライアントに返すことができます。

@Configuration
public class MongoConfiguration {

	public @Bean DB mongoDb() throws Exception {
		MongoClientURI mcUri = new MongoClientURI(getUrlConnection());
		MongoClient mc = new MongoClient(mcUri);
		mc.getDB(mcUri.getDatabase());
		return mc.getDB(mcUri.getDatabase());
	}

UserManager クラスは MongoConfiguration を使用して DB を操作します。init メソッドは “users” コレクションを取得するか、“users” コレクションが存在しない場合は作成します。

private void init(){		
		ApplicationContext ctx = 
	    new AnnotationConfigApplicationContext(MongoConfiguration.class);
		db = (DB) ctx.getBean("mongoDb");
		coll = db.getCollection("users");
	}

MongoDB はリレーショナル DBMS ではなく、ドキュメント指向であることに注意してください。つまり、テーブルの代わりにコレクションがあり、行 (または、タプル) の代わりにドキュメントがあり、列の代わりにフィールドがあります。これらのフィールドは、テーブルの列と同様、あらかじめ定義されているわけではありません。コレクションには、どのような種類のデータでも入力することができます。ドキュメントを検索するには、BasicDBObject を作成する必要があります。

	BasicDBObject user = new BasicDBObject("username", userData.getUsername());
	
	return (DBObject)coll.findOne(user);

UserController は UserManager のクライアントであり、ログインしたユーザーの情報を UserManager の関数を使用して取得し、保存します。

	@RequestMapping(value="/user", method = RequestMethod.GET)
	public  @ResponseBody String getUser(@RequestParam("username") String username, 
@RequestParam("password") String password)  {
		logger.debug("BEGIN: controller getUser - username:" + username);
		UserManager userManager = new UserManager();
		BasicDBObject user = (BasicDBObject) userManager.getUser(username, password);		
		logger.debug("END: controller getUser - user:" + user);

		return user.toString();
	}

ステップ 6. Spring アプリケーションをデプロイする

 

MyVacations は UserService を使用してユーザー情報を取得し、前回ユーザーがログインしたときに UserService が保存した値を使用して検索のプロファイルを作成します。ユーザーには、検索の結果として一連のホテルが地図上に表示されます。またこの場合、コントローラーは xml のコンフィグ・ファイルで構成されるわけではなく、Spring フレームワークによって動的に検出されます。これはサーブレットの構成ファイルに以下のディレクティブが含まれているためです。

     <context:component-scan base-package="com.myvacations.app" />

第 1 のコントローラーである HomeController は、呼び出されるとログイン・ページを表示します。

	/**
	 * Simply selects the home view to render login page.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		
		return "login";
	}

HotelsController はログイン・ページが送信されると有効になります。

このコントローラーは、”username” パラメーターを指定した HTTP GET リクエストによって呼び出されると、UserService にアクセスして、このユーザーが最後にアクセスしたときに保存された、ユーザーの希望に関する情報を取得します。

このコントローラーは RestTemplate を使用して、UserService に対して RESTful な呼び出しを行います。RestTemplate はクライアント・サイドから HTTP でアクセスするための Spring ヘルパー・クラスです。このオブジェクトは getForObject() メソッドとの間で受け渡されて、HttpMessageConverters によって HTTP リクエストへの変換と HTTP レスポンスからの変換が行われます。このサンプル・アプリケーションでは、このクラスを使用して UserService などの RESTful なサービスを呼び出します。

@RequestMapping(value = "/hotels", method = RequestMethod.GET)
public String getHotels(@ModelAttribute("username") String username,Model model) {
		
	logger.debug("BEGIN HotelsController: username=" + username);

	RestTemplate restTemplate = applicationContext.getBean("restTemplate", 
	RestTemplate.class);

	user = (UserData) restTemplate.getForObject(new 
	URI("http://userservice.mybluemix.net/userpref?username="+username), 
	UserData.class);

ステップ 7. サービスを統合する

 

さまざまなサービスを統合して、より多くのデータを MyVacations に追加する必要が出てくると思われるため、ここではサービスを統合する 2 つの例を紹介します。

  • Expedia の RESTful なサービスでホテルの情報を取り込む例。
  • Google マップ・サービスで地図上にホテルを表示する例。

まずは、Expedia の Web サービスを統合してホテルに関する情報を取得してみましょう。始めに Expedia 開発者向け API のサイトにアクセスし、開発者アカウントを登録して API キーを入手します。

SearchController「Submit (送信)」ボタンがクリックされると有効になり、Hotels.jsp の中で Ajax 呼び出しを行います。

    $.get('/search',
      $("#ricerca").serialize(),
      function (data, status) {
        if (status == 'success') {                	
            if (data.toString()==""){
                $("#map_canvas").hide();

SearchController の中で ExpediaClient を呼び出し、HotelSummary リストを取得します。

	List<HotelSummary> response=null;
	response = (new ExpediaClient()).getHotels(
			location, dateFrom, dateTo, numAdults, numChildren);

ExpediaClient は UserService によって取得したユーザー情報を使用して、Expedia の JSON レスポンスをデコードし、ExpediaObjects を抽出します。

Extracting a list of hotels
ExpediaObjects hotels= (ExpediaObjects) 
restTemplate.getForObject(buildQueryHotelsURL(location,dal,al,numAdulti,numBambini),
ExpediaObjects.class);

次にサンプル・アプリケーションは Google マップ・サービスを使用して、Expedia から取得したホテルのリストを地図上に表示します。

Google Maps API を使用すると Google マップの画像を Web ページに埋め込むことができます。作業を始める前に、Google から発行される専用の API キーが必要になります。このキーは無料ですが、このキーを入手するには Google アカウントを作成する必要があります。

<script src="http://maps.googleapis.com/maps/api/js?
key=YOUR_API_KEY&sensor=TRUE_OR_FALSE"></script>

Google Maps API を扱うために、私は jQuery-ui-map を使用することにしました。この優れた jQuery プラグインにより、Web アプリケーションやモバイル・アプリケーションに地図を埋め込むことができます。このプラグインを使用すると、地図やマーカーを表示することや、高度なサービスを利用して、経路の管理、ストリートビュー・モードでの表示、JSON で表された地理データの動的ロードなども行うことができます。

div などの HTML コンテナーを作成したら、jQuery-ui-map プラグインの重要なメソッドである gmap を起動します。gmap を使用すると、Google Maps API の関数を呼び出せるようになり、地図上にマーカーの位置を示す座標を表示することができます。

var map =  $('#map_canvas').gmap({
     'center': new google.maps.LatLng(data[0].latitude,data[0].longitude),
     'minZoom': 5,
     'zoom': 8
         });

ここでは、リストの先頭にあるホテルの地理座標を中心とする地図を作成しました。

今度は検索結果のリストに含まれるすべてのホテルに対してマーカーを作成し、そのマーカーを地図上に表示します。

      $.each(data, function (i, m) {
         $('#map_canvas').gmap('addMarker', {
            'position': new google.maps.LatLng(m.latitude,
                            m.longitude),
            'bounds': true
         }) .click(function () {…

オンクリック・イベントで、簡単なホテルの紹介が情報ウィンドウにロードされるように、関数を登録します。

  $('#map_canvas').gmap('openInfoWindow', {
                                        'content': descr
                                    },

ステップ 8. アプリケーションをクラウドにプッシュする

 

アプリケーションの作成が終わると、このアプリケーションを Bluemix にデプロイすることができます。デプロイメントは自動化されており、ローカルの VM からクラウド・ベースの VM にアプリケーションが移動されます。

デプロイメントを開始するには、Cloud Foundry のCLI で cf push コマンドを使用します。(Cloud Foundry のドキュメントでは、デプロイメント・プロセスは多くの場合、アプリケーションの「プッシュ」と表現されています。)

cf push MyVacations 窶菟ath MYDIR\MyVacations.war

push コマンドによって、さまざまなステージング・タスクが実行されます。ステージング・タスクの例としては、アプリケーションを実行するためのコンテナーの検索、適切なソフトウェアとシステム・リソースを使用することによるコンテナーのプロビジョニング、アプリケーションの (1 つまたは複数の) インスタンスの起動、アプリケーションで想定される状態を Cloud Controller データベースへ格納する動作などがあります。

ステップ 9. アプリケーションをテストする

 

テストとしては、アプリケーション上で簡単な実行テストを行った後で、そのアプリケーションが移植可能かどうかを調べるテストを行います。

単純にするために、ログイン・ページでは任意のユーザー名とパスワードでアクセスできるようにし、システム内にそのユーザーが存在しない場合には、そのユーザーが作成されるようにします。

ログインしたら、検索ページへ進み、検索パラメーターを入力して「Search (検索)」をクリックします。

検索ページと検索結果としての地図のスクリーン・ショット

クリックして大きなイメージを見る

マーカーの 1 つをクリックすると、そのホテルに関する簡単な情報が表示されます。

ホテルに関する簡単な情報を表示したウィンドウのスクリーン・ショット

クリックして大きなイメージを見る

私のアプリケーションを他のクラウド・プラットフォームに移植可能かどうかをテストするために、私は MyVacations アプリケーションを Pivotal プラットフォームと Google App Engine にデプロイすることにしました。

GoogleAE にデプロイするために使用したツールと技術は以下のとおりです。

  1. Google App Engine Java SDK 1.8.8
  2. Spring 3.1.1
  3. Eclipse 4.2 と、Eclipse 用 Google プラグイン

Google App Engine は Spring Framework ベースの Java Web アプリケーションをサポートしているため、私のアプリケーションには何も変更は必要ありません。

(Eclipse にインストールされる) Google App Engine SDK には、ローカルのシミュレーション環境でアプリケーションをテストするための Web サーバーが含まれているため、Google ユーザー・アカウントなしでアプリケーションをテストすることができます。(リモートの Google サーバー上でアプリケーションを実行することもできます。)

Eclipse 用 Google プラグインにより、このサーバーを起動するための項目が「Run (実行)」メニューに追加されます。このシナリオでは、Google App Engine にインストールされた MyVacations が、Bluemix にインストールされた UserService アプリケーションを、RESTful な API を使用して呼び出します。このシナリオにより、内部のプラットフォーム・サービスを使用しないアプリケーションの移植性が高いことが示されます。

localhost で実行される Google App Engine

クリックして大きなイメージを見る

Pivotal にデプロイするために使用したツールと技術は以下のとおりです。

  1. Spring 3.1.1
  2. JDK 7
  3. Spring Tool Suite 3.4.0 と Cloud Foundry Integration for Eclipse 1.5.1

Eclipse 用 Cloud Foundry プラグインを使用すると、Pivotal プラットフォームにデプロイすることができます。この方法だと、ターゲット環境上でアプリケーションを直接テストすることができ、IDE の外部でテストする必要がありません。この方法のためには有効な Pivotal ユーザー・アカウントが必要です。

Pivotal Cloud Foundry へのデプロイメント

クリックして大きなイメージを見る

このプラットフォームには、無料で MyVacations アプリケーションをデプロイすることができ、またわずかな変更をによって UserService アプリケーションをデプロイすることができます。

まとめ

 

このアプリケーションを通じて示したのは、内部サービスや外部サービスをクラウド・アプリケーションと統合することで実現される可能性の一部にすぎません。ここでは、以下に挙げる Bluemix が提供する優れた点のいくつかを利用しました。

  • (アプリケーションやインフラストラクチャーを) プロビジョニングする必要がない
  • スケーラブルである
  • 内部サービスとの統合が容易である
  • 管理が容易である
  • 同様のクラウド・プラットフォームへの移植が可能である

移植性に関しては、Bluemix は Cloud Foundry をベースとしていることから、アプリケーションを他のプラットフォームに自由に移植することができます。以下の 2 つの例で移植性について説明します。

  • MyVacations を Pivotal クラウドにデプロイする場合、何も変更する必要がありません。また Pivotal は Cloud Foundry をベースとしているため、互換性はほぼ完全です。UserService は Bluemix プラットフォーム上で MongoDB サービスを使用します。Pivotal には MongoDB に似たサービスがありますが、そのサービスを利用するには MongoConfiguration クラスの VCAP_SERVICES 変数による URL 接続を変更する必要があります。
  • Cloud Foundry をベースとしていないクラウド (例えば今回の場合では、Google のクラウド) の場合でも、MyVacations のデプロイメントは極めて容易です。(Google プラットフォームによって提供されるサービスには、MongoDB はないため、私は「ビッグ・テーブル」サービスとして別のソリューションを選択しました。このソリューションは MongoDB とは違って独自の方法でデータを永続化します。) 単純に web-inf ディレクトリーに appengine-web.xml ファイルを追加して Google Application Engine を有効にします。以下はその例です。
    <?xml version="1.0" encoding="utf-8"?>
    <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
      <application>_your_app_id_</application>
      <version>1</version>
      <threadsafe>true</threadsafe>
    </appengine-web-app>

最後の注意点として、クラウド・アプリケーションは分散プラットフォームの可能性を活用できるように設計する必要があり、また提供されるサービスは信頼性が高く効率的で高速でなければなりません。この MyVacations アプリケーションでは、その大部分を実現できたと思います。もちろん、大量のリクエストを受信すると、ユーザーに対する UserService の応答は遅くなる可能性があります。しかしその問題については、さまざまなパフォーマンス調整を試すことができます (例えば、レスポンスを受信するまでタスクがブロックされることがないように、非同期メッセージングを使用してコンポーネント同士を分離するなど)。この種のアプリケーションにはいくつかのパフォーマンス・チューニング技術を適用できるので、それらを試しながら楽しんでください。

謝辞

この記事の執筆を励ましてくれるとともに、レビューをしてくださった Fabio Castiglioni 氏に感謝いたします。


関連トピック:Cloud computingJava technologyMongoDB

コメントの追加

注意: HTML コードは、コメント内ではサポートされません。


残り 1000 文字

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology, Cloud computing, Web development
ArticleID=967723
ArticleTitle=Web サービスを統合した、移植可能な Java 旅行アプリを作成する
publish-date=08072014