まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

最初にお断りしておくと、今回紹介する機能は通話の API サービスで、実際に試すと少額ですが以下の料金が発生します。その点ご了承ください:
http://twilio.kddi-web.com/price/index.html



Twilio という変わり種サービスがあります:

http://twilio.kddi-web.com/


↑この URL を見てもわかるように、日本では KDDI ウェブコミュニケーションズ様がサービスを提供しています。

Twilio は「クラウド上の電話 API サービス」です。ウェブ上から電話をかけたり、特定の番号への着信を受けて(自動音声などで)処理したり、ということができるのですが、そのプログラミング用 API が公開されていることで開発者視点からも興味深いサービスです。なお、Twilio API の公式ドキュメントについてはこちらを参照ください:
http://www.twilio.com/docs/api/twiml


更に、最近 IBM がベータ公開している PaaS 環境である BlueMix の中で、この Twilio が連携サービスの1つになっていることが分かりました。BlueMix はアメリカの IBM が提供しているサービスで、日本で取得した Twilio アカウントが使えるか心配でしたが、私が使っている限りでは特に問題ないようです。 というわけで、この BlueMix のサービスとして Twilio を使った電話連携アプリを作る様子を紹介してみます。


BlueMix では Twilio サービスが提供されていますが、使うためにはあらかじめ Twilio のトライアルアカウントを取得しておく必要があります。既にアカウントをお持ちの方は自身のアカウント ID と認証トークンを確認してください。Twilio のアカウント作成に関しては Micorsoft のページに詳しく記載されていたので参照ください。こちらの手順でアカウント ID と認証トークン、そして通話用の電話番号(!)が取得できます:
Twilio (トゥイリオ)を触ってみた

ここで一点注意を。通話用の電話番号はトライアルアカウントの範囲内で取得できますが、その番号では SMS は利用できません。SMS 用の電話番号を取得するには初期チャージ料金 2000 円と、月額10ドルの番号維持費が必要になります。


Twilio のアカウントが取得できたら、そのアカウントを使った Twilio サーバーを BlueMix 上に作成します。まずは BlueMix にログインし、ダッシュボードから "Add a service" を選択します:
2014040401


サービスの種類は "Twilio" を選択します:
2014040402


確認画面で "ADD TO APPLICATION" を選択します:
2014040403


Twilio サービスの簡易設定を行います。といってもここで設定する項目は3つだけで、下の2つは Twilio アカウント作成時やログイン後の画面から取得できるものです。入力後に "CREATE" をクリックします:
 - Add to: アプリケーションサーバーの指定。後で指定する場合は "Do not associate" でも構いません。
 - Account SID: Twilio API を利用する アカウント ID
 - Auth Token: 認証トークン文字列
2014040404


先ほどの手順でアプリケーションサーバーの指定をしなかった場合は別途アプリケーションサーバーを作って(あるいは既存のものを使って)Twilio サーバーと紐づけます。ここでは kkimura3 という名前の Java アプリケーションサーバーを作って、そこに紐づける前提で紹介しています。アプリケーションサーバーの "Add existing service" を選択します:
2014040405


紐づけるサービスを選択します。今回は上記で作成した Twilio サーバーを指定します:
2014040406


アプリケーションサーバーを再起動する、という確認画面が出たら OK をクリックします:
2014040407


アプリケーションサーバーが再起動し、Twilio のマークが表示されればサービスと紐づいていることが確認できます:
2014040408


この紐づいた状態で kkimura3 サーバーの環境変数を確認すると、VCAP_SERVICES 変数に Twilio アカウントや認証トークンが設定されていることが確認できます。したがって、この後作成するアプリケーションにはこれらの情報をハードコーディングする必要がなく、環境変数から取得する、というセキュアな手法も可能になります:
2014040409


また、Twilio API の SDK をダウンロードしておきます。各種言語の API はこちらに用意されています:
Twilio API Libraries

今回は Java を利用するので、ページの真ん中くらいの "Java" というセクションから "twilio-java" と書かれた箇所をクリックし、リンク先から最新版の Java SDK をダウンロードします(2014/04/04 時点では twilio-java-sdk-3.3.16.jar が最新版です)。
2014040410

あとは普通に(Eclipse などで) Java アプリケーションを作成します。その際に今ダウンロードした Twilio Java SDK ファイルをプロジェクトに(WEB-INF/lib/ とかに)インポートして、プロジェクト内で利用できるようにしておきます。さあこれで準備は完了です!


で、Twilio 連携アプリケーションを作るわけですが、今回は以下のような仕様のシンプルなアプリを作ります:
- 「指定の番号へ電話をかけて、特定のメッセージを流す」というアプリ。
- 発信元番号は Twilio で取得した番号(日本で取得した場合は 050-AAAA-BBBB という、050 で始まる番号になっているはずです)。
- 着信先番号はアプリ内で指定する(ただし自分で受け取れないと意味ないので、自分の携帯電話番号とか)。

以下でそのアプリのコードを紹介しますが、かなりシンプルなのがわかると思います。

なお、日本の電話番号はゼロから始まる(0**-****-****)のですが、Twilio から発信する場合は国際電話形式に変更する必要があります。そのため最初のゼロをとり、頭に +81 を付け、+81-**-****-**** という形式の電話番号をコード内で指定している、という点に注意してください。


Twilio 連携をするアプリケーションをこんな感じで作成します。今回は JSP ファイル(twilio.jsp)を1つ作成し、以下のような内容にしてみました:
<%@page import="java.util.*" %>
<%@page import="java.net.*" %>
  :
<%@page import="com.twilio.*" %>
<%@page import="com.twilio.sdk.*" %>
<%@page import="com.twilio.sdk.resource.factory.*" %>
<%@page import="com.twilio.sdk.resource.instance.*" %>

<%
String sid = "", token = "";
String env1 = System.getenv( "VCAP_SERVICES" );
if( env1 != null && env1.length() > 0 ){
  // BlueMix 環境なので、env1 から accountSID と authToken の値を取り出し、sid / token に格納する
    :
}

//. BlueMix でない環境のための設定
if( sid.length() == 0 ){
  sid = "(Twilio のアカウントSID)";
}
if( token.length() == 0 ){
  token = "(Twilio の認証トークン)";
}

String r = "";
try{
  TwilioRestClient client = new TwilioRestClient( sid, token );
  Map params = new HashMap();
  params.put( "To", "+8180xxxxyyyy" );  // +81-80-XXXX-YYYY 形式での(自分が受け取れる)電話番号
  params.put( "From", "+8150aaaabbbb" ); // +81-50-AAAA-BBBB 形式での Twilio で取得した電話番号
  params.put( "Url", "http://demo.twilio.com/welcome/voice/ja/" ); // 確認用ウェルカムメッセージ
  CallFactory callFactory = client.getAccount().getCallFactory();
  Call message = callFactory.create( params );  // 実際に電話をかける
  r = "" + message.getSid();
}catch( Exception e ){
  e.printStackTrace();
  r = "Exception: " + e;
}
%>

result = <%= r %>

ここでの内容はシンプルなので眺めていればなんとなくわかると思います。発信番号と着信番号を指定して、着信するとウェルカムメッセージを流す、というだけの内容です。また成功した場合のウェブ画面には SID が表示されるようになっています。

ちなみに上記コード内の params.put( "Url", "***" ); で指定した箇所ですが、Twilio ではメッセージの内容を特定のフォーマット(TwiML)で定義するのですが、その定義内容が参照できる URL を指定することで、そのメッセージを音声で喋らせることができます。上記で指定しているのはデフォルトの確認用メッセージ(「トライアルアカウントへようこそ・・」みたいな感じ)が流れる URL ですで、その中身は以下のようになっています:
<Response>
<Say language="ja-JP" voice="alice">VOICE U R L の設定を変更することでこの文章を変更できます。</Say>
<Pause length="1"/>
<Say language="ja-JP" voice="alice">仕様のことでご不明点が御座いましたらご連絡ください。</Say>
</Response>

この内容は自分でオリジナルのものを定義/用意することも可能ですし、TwiML を生成/保管するサービスも提供されているようです。TwiML の仕様についてはこちらを参照するか、あるいはウェブで情報を探してみてください。


こうして作成したウェブアプリケーションを WAR ファイル(twilio.war)にして、上記で作成した BlueMix の kkimura3 サーバーに cf を使ってデプロイします(この辺りの詳しい手順はこちらを参照ください):
# cf push kkimura3 -p twilio.war

そして、ウェブブラウザで BlueMix のアプリケーションサーバーの twilio.jsp にアクセスすると、画面には SID が表示されて・・・
2014040411


そして携帯電話には Twilio の番号からの着信が・・・
写真


おお! できました!! このコールを着信すると機械的な声で Twilio のデモメッセージが(自分で作ったメッセージの URL を指定した場合はそのメッセージが)音声で流れてきます。つまりウェブアプリから電話番号に発信してメッセージを流す、というアプリがこんな簡単に作れてしまったことになります。


このアプリは放置しておくと1アクセスごとに電話がかかってきて(しかも19円ずつかかって)しまうので、検証が終わった段階で止めておくのがいいでしょう。でも電話や SMS の連携が数行のコードでできてしまうのはアプリ開発の幅が広がりますよね。

そしてそんな Twilio サービスを自分のアプリ用に、しかも選択するだけで簡単に用意できてしまう BlueMix プラットフォームにも可能性を感じました。




ウェブ制作において、画像やサイトの一部を別モーダルウィンドウでプレビューのように表示する、通称「ライトボックス」と呼ばれる機能があります。 具体的にはこんな感じのものです:

マンホールマップ  ←クリックするとライトボックス内でマンホールマップを表示します

↑CSS が適当なので少し見にくいかも。あとスマホだとうまく表示されないかもしれません・・・


元のページに戻ってくることを前提に、違うページの情報を一瞬表示させる、という目的で便利に使える機能です。Facebook などでも写真を表示する時に使われてますね。こういった機能を実現するための JavaScript ライブラリも多く公開されています(ちなみに上記機能は Colorbox というライブラリを使って実現しています)。

Colorbox で iframe を使った簡単な実装例を紹介します。上記ページから js と css をダウンロードし、jQuery と合わせて <head> 内に以下のような記述をしておきます:
<link  href="css/colorbox.css" rel="stylesheet">
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jquery.colorbox-min.js"></script>
<script type="text/javascript">
$(function(){
	$('.box').colorbox( { iframe:true, width:"90%", height:"90%" } );
});
</script>

この例では box というクラスが付与されたリンクに対してライトボックスを実行するような記述になっています。 
その上で以下のような <a> リンクを用意、これだけです:
<a class="box" href="xxx">XXXX</a>


ところが、このライトボックスが使えないページ(ライトボックスの中身として表示できないページ)、というのがたまに存在しています。例えばグーグルのトップページ(http://www.google.co.jp/)です:

Google  ←クリックするとダイアログは表れますが、中に何も表示されません


実は同じようなページは他にもあります。僕が知る限りではアマゾン価格コムのページもライトボックスでは表示されません。

この違いは何でしょうか? 実はページを HTTP リクエストで取得する際のレスポンスヘッダ X-Frame-Optionsによって、そのコンテンツが iframe の内部に表示することを許可するかどうかを制御できます。上記の挙動の違いはこのレスポンスヘッダ X-Frame-Options の値によるものでした。
2014040600


デフォルト値は「なし」です。その場合(つまり特に何もしない場合)、そのページは iframe 内に表示可能です。一方この値が DENY に設定されていると、そのページを iframe 内に表示することはできません。また SAMEORIGIN(生成元と同じフレーム内に限り表示可)や、ALLOW-FROM xxx(生成元が xxx の場合に限り表示可)といった設定も可能です。詳しくはこちらを参照してください。
2014040601


こういう制限をかける側としてはそれなりの意図があって制限しているんでしょうけど、やはり表示できるものなら表示したい、という作成側の意図もあるわけです。また AJAX のクロスドメイン制限にも言えることですが、制限自体は JavaScript での挙動や iframe 内での挙動に制限をかけているだけであって、情報の取得そのものを制限しているわけではないので、回避することもできます。 で、今日はその(X-Frame-Options 制限の)回避に挑戦してみます。

考え方としては iframe での表示に制限をかけられているサイトに対して、Java や PHP などのサーバーサイドプログラミングによって情報を取得(これらには制限がないので可能)し、その結果を iframe 内に入れてしまおう、というものです。まあクロスドメイン対策と同じ考え方です。
2014040602


ということで、上記の紫色の部分を別途作成する(加えて、それをどこかのサーバーに設置しておく)必要があるわけです。この部分は例えば Java のサーブレットであれば、おおまかにはこんな感じになるでしょうかね:
public class SpoofXFrameOptionsServlet extends HttpServlet {
  public void doGet( HttpServletRequest req, HttpServletResponse res ) throws IOException{
    req.setCharacterEncoding( "UTF-8" );

    String html = "";
    String url = "", _url = req.getParameter( "url" );
    if( _url != null && _url.length() > 0 ) url = _url;
    if( url != null && url.length() > 0 ){
      try{
        GetMethod method = new GetMethod( url );
        HttpClient client = new HttpClient();
        int sc = client.executeMethod( method );
        html = method.getResponseBodyAsString();
      }catch( Exception e ){
        e.printStackTrace();
      }
    }

    res.setContentType( "text/html; charset=UTF-8" );
    res.setCharacterEncoding( "UTF-8" );
    res.getWriter().println( html );
}

このサーブレットが http://xxxx/spoofxframeoptions という名前でアクセスできるようになっていたとすると、
http://xxxx/spoofxframeoptions?url=(iframe 内に入れたいページのURL)

のように指定することで、目的のページのコンテンツを取得できることになります。 なので、この上記の URL を iframe 内で指定すればいい、ということになります。
<iframe class="box" href="http://xxxx/spoofxframeoptions?url=(iframe 内に入れたいページのURL)"></iframe>

で、これを実際にやってみたのがこちらのリンクです:
Google  ←ライトボックス内でGoogleトップページが表示できるようになりました

厳密には上記のサーブレットのサンプルはまだ不完全で、実際には文字コードを考慮したり、HTML 内の <base href="..."> 要素を追加する必要があったり、ページによっては Referer を指定しないと取得できなかったり、、、といった細かな修正が必要になるのですが、とりあえず X-Frame-Options の制約を回避する手段としては使えそうですね。


 

2週間ほど BlueMix を使ってみました。今後も BlueMix 関連のブログエントリを書いていくつもりですが、一度この段階で感じたことなどまとめて書いてみます。なお過去のエントリはこちらです:
- 「BlueMix を使う」
- 「BlueMix のダッシュボードとデータベースを使う」
- 「BlueMix 上で PHP アプリを動かす」


まず「全体的によく出来てる」なあ、と感じました。BlueMix はウェブアプリケーションを「アプリケーション」、データベースなどの周辺機能を「サービス」として分けて提供しており、「アプリケーション」では Java や Node.js、Ruby、そして(標準機能ではないですが)PHP などを動かすことができます。また「サービス」は用意された選択肢から IBM 製品のものも、そうでないものも含めて選ぶだけで追加でき、アプリケーション側でそのサービスに接続するような記述をすることで連携して使える、というものになっています。

開発者視点でのウェブアプリケーション開発というのは、おおまかにはこの「アプリケーション」部分を作ることになるわけで、そこでは各種言語が使えるような柔軟性が提供されています。一方でデータストア等の「サービス」は選んで使うだけ、なので、ほとんど負担がありません。この分離方法は開発者視点でも非常に有用で理にかなっているな、と感じました。

自分の場合は Java アプリと PHP アプリを中心に、自分の手元にあるオンプレミスや IaaS 環境用のアプリがどの程度動くのか、動かすためにどの程度の変更が必要か、を調べながら使ってみましたが、「ウェブアプリに関してはほぼそのまま使える」という印象を持ちました。IaaS 向けのアプリが PaaS の環境で、それも war ファイルが war ファイルのまま(データベースの接続情報さえ変えれば)デプロイして動く、というのは感動すら覚えました。PaaS というと制約の多さや、IaaS/オンプレミス環境から移植性の難しさなどをイメージとして持ってしまいますが、BlueMix ではその辺りはかなり好印象でした。

もちろん、世の中のアプリすべてがウェブアプリケーションだけで完結しているわけではない(スケジュールエージェントやバッチ処理が必要なものもある)ことは理解しています。ただ少なくともウェブ部分に関しては BlueMix で動かすことはさほど高いハードルではないように感じています。


もうひとつ感じたことは「情報が意外と多い」ことです。これは BlueMix の情報が多いというよりは、そのベースとなっている CloudFoundry の情報として見つかることが多いという意味です。この辺りはオープンソースコミュニティの強みと言えますね。ただ現段階ではやはり見つかる情報は英語が多い、という現実があることも付け加えておきます。


一方で、2週間使ってみた上で???な点もあります。まずサービスの中身がよく分からない、という点を挙げたいです。例えば MySQL サーバーを使いたければサービスから MySQL を選ぶだけでいい、確かにその通りです。でもこうして作成された MySQL サーバーのスペックやバックアップ体制、デフォルトで用意されるデータベースの属性(デフォルト言語は UTF-8 になっているのだろうか??など)、、、簡単なのは悪いことではないのですが、簡略化されすぎていて、良くも悪くもブラックボックスに隠れちゃってる(それでいて情報がない)点が多いと思いました。この辺りは CloudFoundry というよりは BlueMix の運用体制に関わる部分なので、IBM からの情報として開示してほしい所ではあります。

あと、現状はオープンベータとして開発者中心に無料で自由に使える素晴らしい環境を提供いただいているのですが、このベータプログラムが終了した後の、実際に商用サービスとして始まる際の情報が少なすぎると感じます。要は「このオープンベータプログラムはいつまで使えるの」「商用版を使うのにいくらかかるの?」ということです。

ちなみに本家 CloudFoundry では「使ったサーバーのメモリ量」をベースに課金されます。例えば 512MB のメモリを搭載したサーバー2台と 1GB のメモリを搭載したサーバー1台を使った場合は 2GB 分の課金がされる、という具合。CPU や I/O、ネットワーク、そして中で使った OS、ソフトウェアへの課金はありません。ある意味でシンプルな課金体系になっています。

※なお、CloudFoundry の現在の具体的な料金は $0.03 /GB/Hour (1GB を1時間使うと3セント)です:
 http://dreamand.me/cloud/cloudfoundry-v2-review/

 仮にメモリ 1GB のサーバー1台を30日間使う場合であれば $0.03 * 24 * 30 = $21.6 という計算になります。


これが BlueMix ではどうなるのでしょう? CloudFoundry と同じような体系になることも想像できますが、そうでない体系になる可能性もあります。またサービスで提供されている機能の中には IBM の商用ソフトウェアも含まれており、これらが無料で提供されるとは考えにくいのですが、ではどういった料金(体系)になるのか? BlueMix には使いやすいダッシュボードや豊富なサービスによる差別化もあると思うのですが、とにかく現状は価格に関する情報が少なすぎる(というか皆無)ので、その辺りの情報が欲しいところではあります。


最後に、今僕自身が「日本で CloudFoundry に詳しい会社ってどこだろう?」と考えた時、真っ先に思いつくのは実際に社内で活用していて、ウェブに資料も多く公開している楽天です。次が実際にサービスを展開しているNTTコミュニケーションズ。残念ながら、現時点では日本アイ・ビー・エムという印象は薄いです。

今後、BlueMix を展開していく中で、日本アイ・ビー・エムがこの国内2強に割って入るような会社として認識されるような存在になってほしい、そのためにもまだ充分とはいえない日本人ユーザー向け情報や資料を BlueMix を推進していく中でどんどん充実させていってほしい、と感じています。













このページのトップヘ