2015年6月21日日曜日

ハイエンドヘッドフォン(イヤフォン)への道
(今となってはミドルエンド?)

唐突ではあるが自分が使用してきた主なイヤフォンを紹介していきたと思う。
ただ、イヤフォンは正直2万を超えるあたりから投資額に見合った変化がなくなっていくと思うので、この先は好みの音にターゲットを絞っていくのが賢明だと思う。そして、その参考になれば幸いです。
とはいえ最近のハイエンド(IE800とかRoxanneとか)が気になり始めてる。

Bose in-ear headphones (TriPort®IE)

一番最初に購入したハイエンドイヤフォン。それまではあまりイヤフォンの性能とかをあまり気にしていなかっただが、会社の後輩から借りて聞いたときの衝撃は今でも忘れられない。それは今まで聴いていた時間をごっそり損していたなと感じるほどに。
このヘッドフォンは、中・低音の重量感が素晴らしい。もっとも、後で知ったことなのだが、このイヤフォンにはパッシブ・イコライザーが内蔵されいるらしく、多分にパッシブ・イコライザーのおかげっぽいんだけど。ただ、このイヤフォンで聴くとBoseらしいというか、どの曲を聴いてもいわゆるBoseサウンドになってしまい、生の音をそのまま聴きたいという用途には向いていないと思う。
また、このイヤフォン良くも悪くも外部の音はほとんど素通り状態。外部の音を遮断したくない時には重宝するが逆に電車とか外部のノイズを遮断したい用途には向かない。

総合★★★☆☆サウンドトラックなどBGMサウンドとしてまったり聴くのには最適
高音★★★☆☆可も不可もなく無難にまとまった音
中低音★★★★☆パッシブイコライザーのおかげで重厚な音が聴ける
解像度★★☆☆☆解像度はあまり高くなくまったり聴かせる感じ
遮音性★☆☆☆☆ほとんどなし
装着感★★★★☆圧迫感がなく長時間つけても違和感を感じない

audio-technica ATH-CK10

Bose in-ear headphonesをしばらく使っていて、元の音をもっと素直に聴かせてくれるイヤフォンが欲しくなって購入したのがこれ。初めてバランスド・アーマチュア型のイヤフォンを購入したんだけど、これがまた衝撃だった。イヤフォンで音の解像度という用語の意味がはじめて分かった気がした。このイヤフォンで曲を聴くと音の一つ一つがはっきりして今まで気が付かなかった音がいっぱいあることを感じた。また、音の立ち上がり(反応)も早く、立ち上がり部分もはっきりと聴こえ、ボーカルとかの息遣いが感じられる。それから、このヘッドフォンの装着感はすばらしく、重量が軽く耳にすっぽりとはまる感じで長時間つけていても全く苦にならない。遮音性も非常に高く電車内でもノイズキャンセル機能なんてこれがあればいらないじゃんと思える。
ただ、このイヤフォンも万能ではなく、音はいわゆるaudio-technicaが得意とするドンシャリ系でよく言えば高音域はきらきらした感じできれいな感じだが、高音が苦手な人は長時間聴いているとちょっと疲れてくる。また、ドライバーが2つしかなく、中高音をターゲットにしているため、低音域は少し物足りない感じが否めない。

総合★★★★☆中高音の音をきらびやかに聴きたいときにおすすめ
高音★★★★☆高音域は解像度も高く素晴らしいが、長時間聴いていると聴き疲れするかも
中低音★★★☆☆中低音は少し物足りない
解像度★★★★★解像度は高い
遮音性★★★★★耳栓レベルの高い遮音性
装着感★★★★★自分が持っているイヤフォンではこれを超える装着感のものはない


audio-technica SOLID BASS ATH-CKS1000LTD

ATH-CK10にそれほど不満はなかったんだけど、やはりインストルメンタル系の曲を聴くと、ATH-CK10どうしても低音域の迫力が足りなくて低音が強く出るものを探している過程で購入したイヤフォン。このイヤフォンは全体的に高レベルにまとまってきれいに音が出るのでとても良い。特に低音域の響きは素晴らしいものがある。ただ、難点があるとするとコードの素材が固いせいかコードをこすってしまった時や風が当たった時にコードからのノイズが入りやすく、通勤時などの歩きながら使う用途にはあまり向いていない。

総合★★★★☆どんなソースでも無難に再現してくれる
高音★★★★☆audio-technicaらしいきらびやかな高音域の音
中低音★★★★★中低音も響きは素晴らしい
解像度★★★★☆解像度もそこそこ高い
遮音性★★★★☆そこそこ高い
装着感★★★★☆ちょっと本体が大きいので長時間つけてると疲れる

JVC HA-FXT90

これは“カーボンナノチューブ振動板”採用というものがどんな音がするのか興味本位で購入しただけなんだけど、聴いた途端スピーカーで聴いているような音圧を感じたのですごい驚いた。もともとイヤフォンで聴くのはあまり好きでなく、外出時以外ではイヤフォンを使用することはほとんどなくスピーカーで聴いているため、最初にこれの音を聴いたときはまさにこういうもの探していただという気持ちだった。バランスド・アーマチュア型に比べれば解像度は低いのは確かなんだけど、このスピーカーで音楽を聴いているという感じがたまらなく好きになり愛用の一品となった。ただ、これを購入した当初、カーボンナノチューブを使用したモデルがこれ(ミドルエンド)しかなく、もう少し解像度も改良されたものがほしいなとは思っていた。


総合★★★☆☆値段の割にはいい音が出ていると思う。なんとちってもスピーカーで来ているような感じが良い
高音★★★☆☆高音域は少し弱い
中低音★★★★☆中低音の響きは素晴らしい
解像度★★★☆☆解像度値段相応
遮音性★★★★☆そこそこ高い
装着感★★★☆☆フィット感は少しもの足りない

JVC HA-FXZ200 LIVE BEAT

JVC HA-FXT90の発売後、待望の“カーボンナノチューブ振動板”採用のハイエンドモデル。そして今一番のお気に入り。HA-FXT90で弱かった高音域の音や音の解像度も向上しかなりいい感じに仕上がったモデル。音の音圧感もさらに強化されよりスピーカーで聴いているような感じが強くなった。もちろんバランスド・アーマチュア型に比べれば解像度は低いのでモニタ用途には向いていないとは思うけど。
ただ、スピーカーで音楽を聴くような感じで聴く分には申し分ないと思う。


総合★★★★★個人的には一番のおすすめ(好み)
高音★★★★☆高音域は少し弱い
中低音★★★★★中低音の響きは素晴らしく力強い
解像度★★★★☆解像度もハイエンドとしてなかなか高いレベル
遮音性★★★★☆そこそこ高い
装着感★★★★☆フィット感は少しもの足りなく、個人的にこのイヤフォンの唯一の弱点だと思ってる

2014年8月23日土曜日

遺伝子検査のMYCODEを申し込んで(注文して)みた ― 結果編

遺伝子検査のMYCODEの結果が届いたので簡単にレポートする。
(申込みに関する内容は、申込み編を参照してください。)

概要

私が申し込んだ「オールインワン280+」の検査レポートには、下記2つに分類される項目に対し、その項目に関係する遺伝子情報による評価と、それらの評価を踏まえた生活改善アドバイスが含まれている。生活改善アドバイスは、MYCODEのマイページにある生活習慣アンケートをWeb上で回答することにより、より自分に合ったアドバイスになる。
  • 発症リスク(152項目[2014年8月時点])
    検査レポートでは、がん、生活習慣病など、全部で約150種類の疾患についての発症リスクが日本人平均との比較して何倍であるかが、下図のように示される。
  • 体質(130項目[2014年8月時点])
    検査レポートでは、見た目の特徴、食習慣、血液・代謝関連の項目など、約130種類の体質の遺伝的な傾向が3段階評価として、下図のように示される。
※実際の検査項目は、公式HPの「検査メニュー」のページのメニューごとの「もっと詳しく」で参照できます。

自分は申し込まなかったけど、Web上では有料オプションとして管理栄養士にWeb上で直接相談することができる「生活改善プログラム」というのもある。

また、うれしいことに、今後検査レポートに対し、新たな検査項目を追加したり、すでに結果が出ている項目を最新のもに更新するということを予定しているらしい。


内容について

検査レポートの発症リスクの中で比較的高かった項目(1.80倍以上)は、以下の項目だった。親戚にはがんで亡くなった方が何人かいるのでもっとがん関連の発症リスクが高くでるとおもったけどそうでもなかった。ただ、血液関連の発症リスクは比較的高いように感じた。
それと、円形脱毛症はちょっと気になるな。。。

抗好中球細胞質抗体(ANCA)関連血管炎
心臓・循環器

2.83倍
ウェゲナー肉芽腫
内分泌・血液・代謝

2.77倍
食道がん
がん

2.61倍
円形脱毛症
その他

1.94倍
アルコールとニコチンの共依存症
その他

1.93倍
関節リウマチ
骨・関節

1.87倍
全身性エリテマトーデス(SLE)
骨・関節

1.80倍

実際の発症リスクのレポートにはリスク数値以外に、解析したDNAの遺伝子型の説明や予防や発症の促進につながるとされる環境要因などが解説されており、とても参考になる。
どういった内容かは公式HPの「検査メニュー」のページの下の方に「サンプルレポート」があるのでそちらを参照するとよいと思う。

それから体質の項目には、「見た目の特徴」、「食習慣」、「血液・代謝」などの分類があるのだが、その中でちょっとおもしろいなと思った項目には以下のようなものがあった。

  • 見た目の特長
    • 目の機能(近視や遠視の指標)
    • 小麦色の日焼けのしやすさ
    • 目の色(青に近いかどうか)
    • そばかすのできやすさ
    • 胸のサイズ
  • 食習慣
    • アルコールによる顔の赤くなりやすさ
    • 飲酒傾向
    • 苦味(PROP)の感じやすさ
  • 血液
    • 血圧の高さ
    • 白血球の数
    • ヘモグロビンの量
    • 血小板の数
    • 血糖値の高さ(空腹時)


感想

発症リスクとかは、正直もっと偏るのかと思ったけど案外平均に近いんだなというのが正直な思い。ただ、自身の体の傾向がわかるのでいろいろ生活改善には役立つ気はする。
それから、検査レポートの遺伝子の説明や疾患に関する説明は見ていてなかなか興味深いものがあり、それだけでも価値があるかも。


2014年8月19日火曜日

遺伝子検査のMYCODEを申し込んで(注文して)みた ― 申し込み編

遺伝子検査に興味があったので、DeNAで始まった遺伝子検査サービス(以下MYCODE)に早速申し込んでみた。
MYCODEの遺伝子検査は唾液を専用容器に入れて郵送するだけで検査することができ、検査項目数によって下記3つのメニューが用意されている(詳細はこちら)。
  • オールインワン280+
    病気(がん・生活習慣病)、体質・ダイエット・美容の全検査項目が入ったセット

  • ヘルスケア100+
    がん・生活習慣病の主たる病気と、体質・ダイエット・美容の一部検査が入ったセット

  • カラダ30+
    ダイエット・肥満、栄養、代謝、体型、美容(髪質・肌)、等体質の主要項目が入ったセット

検査は、検査キットを購入するところから始まり、次のような流れになり、検査状況や検査結果は、Web上で確認することとなる。

  1. 検査キットの購入
  2. MYCODEの公式サイトでの会員登録
  3. 遺伝子検査申込書兼同意書/検査コードの登録/唾液採取
  4. 遺伝子検査申込書兼同意書 及び 唾液採取済みの採取容器の送付

検査キットの購入

Amazon(上記リンク)、または、MYCODEの公式サイトで注文することができる。注文すると2~3日くらいで下図のような検査キットが送付されてくる(とりあえず私は先行予約でオールインワン280+を注文)。

遺伝子検査申込書兼同意書/検査コードの登録/唾液採取

検査キットが送付されてきたら、検査キットに同梱されている「遺伝子検査申請書兼同意書」の右下に貼られたシールに記載されいる(検査ガイドに記載されているの例なので間違えないように!)検査コード(3桁-3桁-4桁の数字)をMYCODEのマイページから「登録する」ボタンをクリックし、検査コードを登録する。

次に検査キットに同梱されている「MYCODE遺伝子検査申込付属書」「重要事項確認書」を熟読の上、「遺伝子検査申請書兼同意書」を記入する。「遺伝子検査申請書兼同意書」には、検査を申込む本人に関する情報(住所、電話番号、生年月日、性別)と申込み(購入した)検査メニュー、並びに、検査申込みに関する内容に同意する旨の署名を記入する。
加えて、「研究へのご協力のお願い」への「同意する・しない」(資料及び検査結果を研究に使用してよいかどうか)を記入する(私は同意することにした)。

そして、いよいよ唾液採取セット(下図)により唾液(遺伝子情報の検体)を採取する。検査キットには、唾液を出しやすくするためにレモンの写真(カード)が付いている(笑)。こんな写真いらないんじゃないかと思ったが、思った以上に唾液が必要で、意外と活躍した。


※ここら辺の詳細な手順は、同梱の「検査ガイド」に記載されている。

遺伝子検査申込書兼同意書 及び 唾液採取済みの採取容器の送付

最後に、検査キットに同梱されている返信用封筒に唾液採取済みの採取容器と「遺伝子検査申請書兼同意書」の1枚目(返送用)をいれ、ポストに投函する。返信用封筒の裏面はチェックシートになっているのでそちらを活用する。

※「遺伝子検査申請書兼同意書」

返信用封筒がMYCODEの解析センターに届くと「【MYCODE】検査を開始します」という件名の受領した旨のメールが届く。
そして、2~3週間後に結果が出る(「【MYCODE】検査が完了しました」という件名のメールが届く)らしい。

2014/08/23 8/22に結果が届いたので、結果編をアップしました。検査開始のメールが8/18届いたので実質4日間で検査結果が出たことになるな。

2014年2月16日日曜日

JavaによるProduct Advertising APIのSOAP呼び出し(WS-Securityを利用してSOAPリクエストを処理する方法)

なにかアフィリエイトのサイトを作ろうと、まずは、JavaによるProduct Advertising APIのSOAP呼び出し方法を調べてみた。
ドキュメントには、言語に特化した詳しい説明があまりないので思った以上に苦労したので、その内容(主にJavaによる認証部分の実装手順)を備忘録代わりに残しておこうと思う。
(ただし、利用しているツール、ライブラリ、及びProduct Advertising APIの詳細な内容にはあまり触れないのでそれぞれのサイトを確認してください。)

まず、Product Advertising APIをSOAPによる呼び出し方法(認証の方式)には次の2種類がある。

  • WS-Securityを利用せずにSOAPリクエストを処理する方法
  • WS-Securityを利用してSOAPリクエストを処理する方法

本稿では、WS-Securityを利用してSOAPリクエストを処理する方法を説明する。この方法は、WS-Security1.0を利用し認証を行う。JDK単体ではWS-Seurityまでサポートされていないようなので、JAX-WSの実装としてApache CXFを利用する。また、署名に使用する証明書は、keytoolが公開鍵と秘密鍵が別になった証明書のインポートができない(ここは調査不足の可能性も)ようなので、amazonから提供される証明書ではなく、keytoolで生成した自己署名証明書を使用する。

なお、「WS-Securityを利用せずにSOAPリクエストを処理する方法」はこちらを参照。
今回使用したツール、ライブラリは以下の通りです。

ツール、ライブラリ名バージョン

Java Development Kit
7u51(1.7.0_51)
Eclipse IDE for Java EE Developers
4.3.1
Apache CXF
2.7.8
Tomcat
7.5.0

(1)自己署名証明書(秘密鍵・公開鍵のキーペア)の準備


amazonで有効なアルゴリズムはRSAなので、キーアルゴリズムとしてRSAを指定し、自己署名証明書(秘密鍵・公開鍵のキーペア)を生成する。有効期間は、amazonで生成可能な証明書の期間と同じ1年(365日)を設定している。
>keytool -genkeypair -keyalg RSA -dname <X.500識別名> -alias <キーエイリアス> -keypass <キーパスワード> -keystore <キーストアファイル> -storepass <キーストアパスワード> -validity 365

次に生成した自己署名証明書(秘密鍵・公開鍵のキーペア)から、amazonにアップロードするための公開鍵を同じくkeytoolを使用しRFC 1421 証明書符号化のフォーマット(-rfcオプション)で生成する。

>keytool -exportcert -alias <キーエイリアス> -keystore  <キーストアファイル> -file  <公開鍵ファイル> -storepass <キーストアパスワード> -rfc

生成された公開鍵ファイルは、AWS Management ConsoleのSecurity Credentialsからamazonにアップロードする。

X.500識別名
とりあえず適当に"cn=<名前(英字)>, c=JP"とか指定しておく。
キーエイリアス
証明書(秘密鍵・公開鍵のキーペア)にアクセスするためのエイリアス名
キーストアパスワード
キーストアにアクセスするためのパスワード
キーパスワード
証明書(秘密鍵)にアクセスするためのパスワード
キーストアファイル
証明書(秘密鍵・公開鍵のキーペア)を格納するキーストアファイル
キーストアファイル
amazonにアップロードする公開鍵ファイル(RFC 1421 証明書符号化のフォーマット)
※ここで設定したキーストアファイル、キーストアパスワード、キーエイリアス、キーパスワードは、実際のプログラムでも使用するため、以降ここで定義した名前を使用する。

(2)EclipseにApache CXF、Tomcatを設定する

Apache CXFによりコード生成をするため、まずEclipseにApache CXFの設定を行う。また、クライアントコードを生成するだけなのに、なぜかDynamic web Project(Web Serverの実装がないと)でないとApache CXFのコード生成ができないようなのでTomcatも併せてEclipseに設定する。

Apache CXFの設定
Apache CXFをダウンロードし、適当な場所に展開する。Eclipseの「Window」→「Preferences」→「Web Services」→「CXF 2.x Preferences」を開き、「add」ボタンを押下し、展開した場所を設定する。

追加後、一覧に追加したApache CXFの実装がバージョンと共に表示されるので、追加したApache CXFにチェックを入れる。そのほかの設定はとりあえずデフォルトのままでよい。


Tomcatの設定
同様にTomcatダウンロードし、適当な場所に展開する。Eclipseの「Window」→「Preferences」→「Server」→「Runtime Environments」を開き、「add」ボタンを押下する。


New Server Runtime Environmentが開くので「Apache」→「Apache Tomcat v7.0」を選択し、展開した場所を設定する。


追加後、一覧に追加したTomcatの実装がバージョンと共に表示される。


(3)WSDLからSOAPのクライアントコードを生成する


プロジェクト(Dynamic Web Project)の作成
クライアントコードを生成するためプロジェクトをEclipseのDynamic Web Projectとして作成し、プロジェクトのPropertiesを開き、「Java Build Path」の「Add Library」を押下し、「CXF Runtime」を追加する。


追加後、一覧に追加したApache CXF Libraryが表示される。


クライアントコードの生成
Eclipseの「File」→「New」→「Other」を選択し、Newウィザードで「Web Services」→「Web Service Client」を選択する。「Web Service Client」ウィザードが開くので、「Server definition」にProduct Advertising APIのWSDLのURLを設定し、「Configuration」のServer Runtimeに「Tomcat v7.0 Server」、Web service runtimeに「Apache CXF 2.x」を設定し「Next」をクリックする。


次に、WSDL2Javaの設定ウィザードが開き、自動生成されたパッケージ名が「com.amazon.webservices.awsecommerceservice.2011-08-01」となっており、このままだとJavaの規約違反となるため「com.amazon.webservices.awsecommerceservice._2011_08_01」に変更し「Finish」ボタンを押下する。


「Finish」ボタンを押下後、パッケージcom.amazon.webservices.awsecommerceservice._2011_08_01配下に次のソースが生成される。

Accessories.java
Arguments.java
AWSECommerceService.java
AWSECommerceServicePortType.java
AWSECommerceServicePortTypeImpl.java
AWSECommerceServicePortTypeImpl1.java
AWSECommerceServicePortTypeImpl10.java
AWSECommerceServicePortTypeImpl2.java
AWSECommerceServicePortTypeImpl3.java
AWSECommerceServicePortTypeImpl4.java
AWSECommerceServicePortTypeImpl5.java
AWSECommerceServicePortTypeImpl6.java
AWSECommerceServicePortTypeImpl7.java
AWSECommerceServicePortTypeImpl8.java
AWSECommerceServicePortTypeImpl9.java
AWSECommerceServicePortType_AWSECommerceServicePortCA_Client.java
AWSECommerceServicePortType_AWSECommerceServicePortCN_Client.java
AWSECommerceServicePortType_AWSECommerceServicePortDE_Client.java
AWSECommerceServicePortType_AWSECommerceServicePortES_Client.java
AWSECommerceServicePortType_AWSECommerceServicePortFR_Client.java
AWSECommerceServicePortType_AWSECommerceServicePortIN_Client.java
AWSECommerceServicePortType_AWSECommerceServicePortIT_Client.java
AWSECommerceServicePortType_AWSECommerceServicePortJP_Client.java
AWSECommerceServicePortType_AWSECommerceServicePortUK_Client.java
AWSECommerceServicePortType_AWSECommerceServicePortUS_Client.java
AWSECommerceServicePortType_AWSECommerceServicePort_Client.java
Bin.java
BrowseNode.java
BrowseNodeLookup.java
BrowseNodeLookupRequest.java
BrowseNodeLookupResponse.java
BrowseNodes.java
Cart.java
CartAdd.java
CartAddRequest.java
CartAddResponse.java
CartClear.java
CartClearRequest.java
CartClearResponse.java
CartCreate.java
CartCreateRequest.java
CartCreateResponse.java
CartGet.java
CartGetRequest.java
CartGetResponse.java
CartItem.java
CartItems.java
CartModify.java
CartModifyRequest.java
CartModifyResponse.java
Collections.java
CorrectedQuery.java
CustomerReviews.java
DecimalWithUnits.java
EditorialReview.java
EditorialReviews.java
Errors.java
HTTPHeaders.java
Image.java
ImageSet.java
Item.java
ItemAttributes.java
ItemLink.java
ItemLinks.java
ItemLookup.java
ItemLookupRequest.java
ItemLookupResponse.java
Items.java
ItemSearch.java
ItemSearchRequest.java
ItemSearchResponse.java
LoyaltyPoints.java
Merchant.java
NewReleases.java
NonNegativeIntegerWithUnits.java
ObjectFactory.java
Offer.java
OfferAttributes.java
OfferListing.java
Offers.java
OfferSummary.java
OperationRequest.java
OtherCategoriesSimilarProducts.java
package-info.java
Price.java
Promotion.java
Promotions.java
Property.java
RelatedItem.java
RelatedItems.java
Request.java
SavedForLaterItems.java
SearchBinSet.java
SearchBinSets.java
SearchResultsMap.java
SimilarityLookup.java
SimilarityLookupRequest.java
SimilarityLookupResponse.java
SimilarProducts.java
SimilarViewedProducts.java
StringWithUnits.java
TopItemSet.java
TopSellers.java
Tracks.java
VariationAttribute.java
VariationDimensions.java
Variations.java
VariationSummary.java

(4)Product Advertising APIを呼び出す

Product Advertising APIを呼び出すときに、WS-Securityを利用するためのApache WSS4J用のプロパティファイル(client_sign.properties)と、暗号化を行う際に、(1)で生成した証明書(秘密鍵)へアクセスするためのキーパスワードを設定するコールバッククラス(ClientPasswordCallback)を先に作成する。

Apache WSS4J用のプロパティファイル(client_sign.properties)
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=<キーストアパスワード>
org.apache.ws.security.crypto.merlin.keystore.alias=<キーエイリアス>
org.apache.ws.security.crypto.merlin.keystore.file=<キーストアファイル>

コールバッククラス(ClientPasswordCallback)クラス
import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;

public class ClientPasswordCallback implements CallbackHandler {

    /** 証明書(秘密鍵)にアクセスするためのキーパスワード */
    private static String keyPassword = "<キーパスワード>";

    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {

        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

        // キーパスワードの設定
        pc.setPassword(keyPassword);
    }

}

最後にProduct Advertising APIを実行するmain()メソッドを持つクラスを実装する。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.ws.WebServiceRef;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.ws.security.handler.WSHandlerConstants;

import com.amazon.webservices.awsecommerceservice._2011_08_01.AWSECommerceService;
import com.amazon.webservices.awsecommerceservice._2011_08_01.AWSECommerceServicePortType;
import com.amazon.webservices.awsecommerceservice._2011_08_01.Item;
import com.amazon.webservices.awsecommerceservice._2011_08_01.ItemSearch;
import com.amazon.webservices.awsecommerceservice._2011_08_01.ItemSearchRequest;
import com.amazon.webservices.awsecommerceservice._2011_08_01.ItemSearchResponse;

public class AmazonService {

    @WebServiceRef
    private static AWSECommerceService AWSECommerceService;

    /** WS-Security(シグネチャのキー) */
    private final static String WS_SECURITY_SIGNATURE_KEY_IDENTIFIER = "DirectReference";
    /** WS-Security(シグネチャのキーアルゴリズム) */
    private final static String WS_SECURITY_SIGNATURE_ALGORITHM = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";

    /** AWS アクセスキー識別子 */
    private static String awsAccessKeyId = "xxxxxxxxxxxxxxxxxxxx";
    /** amazonアソシエイトのアカウントID */
    private static String associateTag = "XXXXXXXXXX-nn";
    /** 共通のリクエストパラメータ(Validate) */
    private static String validate = "False";
    /** 共通のリクエストパラメータ(XMLEscaping) */
    private static String xmlEscaping = "Single";

    public static void main(String[] args) throws Exception {

        // サービスのエンドポイントの取得
        AWSECommerceService = new AWSECommerceService();
        AWSECommerceServicePortType endPoint = AWSECommerceService
                .getAWSECommerceServicePortJP();
        Client client = ClientProxy.getClient(endPoint);

        // WS-Securityを使用するためのプロパティ設定
        Map<String, Object> outProps = new HashMap<String, Object>();
        outProps.put(WSHandlerConstants.USER, "<キーエイリアス>");
        outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.TIMESTAMP
                + " " + WSHandlerConstants.SIGNATURE);
        outProps.put(WSHandlerConstants.SIG_KEY_ID,
                WS_SECURITY_SIGNATURE_KEY_IDENTIFIER);
        outProps.put(WSHandlerConstants.SIG_ALGO,
                WS_SECURITY_SIGNATURE_ALGORITHM);
        outProps.put(WSHandlerConstants.SIG_PROP_FILE, "client_sign.properties");
        outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
                ClientPasswordCallback.class.getName());
        WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
        client.getOutInterceptors().add(wssOut);

        // ItemSearch用のリクエストオブジェクトの生成
        List<ItemSearchRequest> request = new ArrayList<ItemSearchRequest>();
        String marketplaceDomain = null;
        ItemSearchRequest itemSearchRequest = new ItemSearchRequest();
        itemSearchRequest.setKeywords("新居昭乃");
        itemSearchRequest.setAvailability("Available");
        itemSearchRequest.setSearchIndex("Blended");
        itemSearchRequest.getResponseGroup().add("Medium");

        itemSearchRequest.getResponseGroup().add("ItemAttributes");
        request.add(itemSearchRequest);

        ItemSearch itemSearch = new ItemSearch();
        itemSearch.setAssociateTag(associateTag);
        itemSearch.setAWSAccessKeyId(awsAccessKeyId);
        itemSearch.setMarketplaceDomain(marketplaceDomain);
        itemSearch.setXMLEscaping(xmlEscaping);
        itemSearch.setValidate(validate);
        itemSearch.getRequest().add(itemSearchRequest);

        // サービス(itemSearch)の実行
        ItemSearchResponse itemSearchResponse = endPoint.itemSearch(itemSearch);

        // 検索結果の表示
        for (Item item : itemSearchResponse.getItems().get(0).getItem()) {
            System.out.println(item.getItemAttributes().getTitle());
            System.out.println("\t" + item.getDetailPageURL());
        }

    }

}

41~45行目
(1)で生成したサービスクラスから、エンドポイント(ここでは日本サイト)を取得する。
47~60行目
WS-Securityを使用するためのプロパティの設定する。WS-Securityのアクション(WSHandlerConstants.ACTION)には、WSHandlerConstants.TIMESTAMPとWSHandlerConstants.SIGNATUREを指定し、WS-Securityのユーザー(WSHandlerConstants.USER)には、(1)で生成したキーストアの証明書のキーエイリアスを指定し、シグネチャを生成するアルゴリズム(WSHandlerConstants.SIG_ALGO)には「http://www.w3.org/2000/09/xmldsig#rsa-sha1」を指定し、シグネチャーの識別子(WSHandlerConstants.SIG_KEY_ID)にはメッセージに埋め込む方式の「DirectReference」を指定する。
62~80行目
itemSearchを呼び出すのに必要なオブジェクトを初期化・設定する。設定する内容はProduct Advertising APIを参照。
82~83行目
itemSearchの実行
85~89行目
検索結果を表示
これで、WS-Securityを利用する方法で実行が可能。
実際のSOAP呼び出しの際、SOAPメッセージに必要な要素がない場合、だいたい400:Bad Requestとなり、IDなどの認証情報に誤りがある場合403:Forbiddenとなる。

2014年2月15日土曜日

JavaによるProduct Advertising APIのSOAP呼び出し(WS-Securityを利用せずにSOAPリクエストを処理する方法)

なにかアフィリエイトのサイトを作ろうと、まずは、JavaによるProduct Advertising APIのSOAP呼び出し方法を調べてみた。
ドキュメントには、言語に特化した詳しい説明があまりないので思った以上に苦労したので、その内容(主にJavaによる認証部分の実装手順)を備忘録代わりに残しておこうと思う。
(ただし、利用しているツール、ライブラリ、及びProduct Advertising APIの詳細な内容にはあまり触れないのでそれぞれのサイトを確認してください。)

まず、Product Advertising APIをSOAPによる呼び出し方法(認証の方式)には次の2種類がある。

  • WS-Securityを利用せずにSOAPリクエストを処理する方法
  • WS-Securityを利用してSOAPリクエストを処理する方法

本稿では、WS-Securityを利用せずにSOAPリクエストを処理する方法を説明する。この方法は、AWS アクセスキー識別子のHMAC-SHA256によるハッシュ情報をSOAPヘッダに埋め込む方法で、Javaの標準機能(JAX-WS)だけで簡単に実装が可能(ただ、実装上Base64エンコードが必要となるため、
その部分だけは、Apache Commons Codecを使用しました)。なお、WS-Securityを利用してSOAPリクエストを処理する方法はこちらを参照。
今回使用したツール、ライブラリは以下の通りです。

ツール、ライブラリ名バージョン

Java Development Kit
7u51(1.7.0_51)
Eclipse IDE for Java EE Developers
4.3.1
Apache Commons Codec
1.3


(1)WSDLからSOAPのクライアントコードを生成する

JDKに付属しているwsimportを使用し下記コマンドを実行し、Product Advertising APIが公開されているWSDLからクライアントコードを生成する。あとで、生成したコードを一部修正するので、ここではソースコードのみ生成する。
(WSDLのURLは、日本語ドキュメントだと内容が古いままの箇所があるので、英語ドキュメントも確認すること。)

>wsimport -Xnocompile -s . <Product Advertising API WSDLのURL>

実行後、パッケージcom.amazon.webservices.awsecommerceservice._2011_08_01配下に次のソースが生成される。

Accessories.java
Arguments.java
AWSECommerceService.java
AWSECommerceServicePortType.java
Bin.java
BrowseNode.java
BrowseNodeLookup.java
BrowseNodeLookupRequest.java
BrowseNodeLookupResponse.java
BrowseNodes.java
Cart.java
CartAdd.java
CartAddRequest.java
CartAddResponse.java
CartClear.java
CartClearRequest.java
CartClearResponse.java
CartCreate.java
CartCreateRequest.java
CartCreateResponse.java
CartGet.java
CartGetRequest.java
CartGetResponse.java
CartItem.java
CartItems.java
CartModify.java
CartModifyRequest.java
CartModifyResponse.java
Collections.java
CorrectedQuery.java
CustomerReviews.java
DecimalWithUnits.java
EditorialReview.java
EditorialReviews.java
Errors.java
HTTPHeaders.java
Image.java
ImageSet.java
Item.java
ItemAttributes.java
ItemLink.java
ItemLinks.java
ItemLookup.java
ItemLookupRequest.java
ItemLookupResponse.java
Items.java
ItemSearch.java
ItemSearchRequest.java
ItemSearchResponse.java
LoyaltyPoints.java
Merchant.java
NewReleases.java
NonNegativeIntegerWithUnits.java
ObjectFactory.java
Offer.java
OfferAttributes.java
OfferListing.java
Offers.java
OfferSummary.java
OperationRequest.java
OtherCategoriesSimilarProducts.java
package-info.java
Price.java
Promotion.java
Promotions.java
Property.java
RelatedItem.java
RelatedItems.java
Request.java
SavedForLaterItems.java
SearchBinSet.java
SearchBinSets.java
SearchResultsMap.java
SimilarityLookup.java
SimilarityLookupRequest.java
SimilarityLookupResponse.java
SimilarProducts.java
SimilarViewedProducts.java
StringWithUnits.java
TopItemSet.java
TopSellers.java
Tracks.java
VariationAttribute.java
VariationDimensions.java
Variations.java
VariationSummary.java

(2)AWS アクセスキー識別子のHMAC-SHA256によるハッシュ情報をSOAPヘッダを埋め込むSOAPハンドラを実装する

AWS アクセスキー識別子のHMAC-SHA256によるハッシュ情報をSOAPヘッダを埋め込むSOAPハンドラを実装する。SOAPヘッダには次の3つの要素を埋め込む必要がある。
AWSAccessKeyId
AWS アクセスキー識別子(Product Advertising APIのアカウントID)
Timestamp
リクエストに含めるタイムスタンプ
Signatur
AWS 秘密キー(Product Advertising APIのアカウントIDに対応する秘密キー)を元に、Action および Timestamp パラメータの連結した文字列のHMAC-SHA256のハッシュ値

SOAPヘッダイメージ
<S:Header xmlns:aws="http://security.amazonaws.com/doc/2007-01-01/">
 <aws:AWSAccessKeyId">xxxxxxxxxxxxxxxxxxxx</aws:AWSAccessKeyId">
 <aws:Timestamp">2014-02-15T10:07:43Z</aws:Timestamp">
 <aws:Signature">zI0LD8X7yclKaBzk0b5g8L0G5P2TbC3ncxCD2ng2J3E=</aws:Signature">
</S:Header">

実際の実装は、SOAPのメッセージ・ハンドラ(AWSECommerceServiceProxyHandler) クラスを実装し、SOAPのメッセージ・ハンドラのhandleMessage()メソッド内でヘッダへの埋め込み処理を実装する。
package com.amazon.webservices.awsecommerceservice._2011_08_01;

import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Set;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.namespace.QName;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

import org.apache.commons.codec.binary.Base64;

public class AWSECommerceServiceProxyHandler implements
        SOAPHandler<SOAPMessageContext> {

    /** シグネチャ(ハッシュ計算)用のアルゴリズム */
    private static final String SIGNATURE_ALGORITHM = "HmacSHA256";
    /** AWSの認証用のネームスペース */
    private static final String AWS_SECURITY_NS = "http://security.amazonaws.com/doc/2007-01-01/";
    /** AWSの認証用のネームスペースのプレフィックス */
    private static final String AWS_SECURITY_NS_PREFIX = "aws";

    /** AWSの認証用のヘッダパラメータ(AWSAccessKeyId) */
    private static final String AWS_HEADER_AWS_PARAMETER_ACCESSKEYID = "AWSAccessKeyId";
    /** AWSの認証用のヘッダパラメータ(Timestamp) */
    private static final String AWS_HEADER_AWS_PARAMETER_TIMESTAMP = "Timestamp";
    /** AWSの認証用のヘッダパラメータ(Signature) */
    private static final String AWS_HEADER_AWS_PARAMETER_SIGNATURE = "Signature";

    /** AWS アクセスキー識別子 */
    private String awsAccessKeyId = null;
    /** AWS 秘密キー */
    private String awsSecretKey = null;
    /** 秘密鍵 */
    private SecretKeySpec keySpec = null;
    /** メッセージ認証コード */
    private Mac mac = null;

    /**
     * コンストラクタ
     */
    public AWSECommerceServiceProxyHandler() {
        init();
    }

    @Override
    public void close(MessageContext mc) {
    }

    @Override
    public boolean handleFault(SOAPMessageContext mc) {
        return false;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext mc) {

        // アウトバウンド以外のメッセージは無視する。
        if (!((Boolean) mc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY))) {
            return false;
        }

        String action = getAction(mc);
        String timestamp = getTimestamp();
        String signature = null;
        try {
            signature = calculateSignature(action, timestamp);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        try {
            SOAPMessageContext smc = (SOAPMessageContext) mc;
            SOAPMessage message = smc.getMessage();
            SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
            // メッセージにSOAPヘッダーが存在していない場合、SOAPヘッダを生成する。
            if (envelope.getHeader() == null) {
                envelope.addHeader();
            }
            SOAPHeader header = envelope.getHeader();
            header.addNamespaceDeclaration(AWS_SECURITY_NS_PREFIX,
                    AWS_SECURITY_NS);

            Name awsidName = envelope.createName(
                    AWS_HEADER_AWS_PARAMETER_ACCESSKEYID,
                    AWS_SECURITY_NS_PREFIX, AWS_SECURITY_NS);
            Name tsName = envelope.createName(
                    AWS_HEADER_AWS_PARAMETER_TIMESTAMP, AWS_SECURITY_NS_PREFIX,
                    AWS_SECURITY_NS);
            Name sigName = envelope.createName(
                    AWS_HEADER_AWS_PARAMETER_SIGNATURE, AWS_SECURITY_NS_PREFIX,
                    AWS_SECURITY_NS);

            SOAPHeaderElement akidElement = header.addHeaderElement(awsidName);
            SOAPHeaderElement tsElement = header.addHeaderElement(tsName);
            SOAPHeaderElement sigElement = header.addHeaderElement(sigName);

            akidElement.addTextNode(awsAccessKeyId);
            tsElement.addTextNode(timestamp);
            sigElement.addTextNode(signature);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return true;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Set<QName> getHeaders() {
        return java.util.Collections.EMPTY_SET;
    }

    /**
     * 初期化メソッド
     */
    private void init() {

        // AWS アクセスキー識別子、AWS 秘密キーの設定(必要に応じて外出しにする)
        awsAccessKeyId = "xxxxxxxxxxxxxxxxxxxxxx";
        awsSecretKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

        try {
            byte[] bytes = awsSecretKey.getBytes("UTF-8");
            this.keySpec = new SecretKeySpec(bytes, SIGNATURE_ALGORITHM);
            this.mac = Mac.getInstance(SIGNATURE_ALGORITHM);
            this.mac.init(keySpec);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * エンドポイントのURLからAction名を取得する。
     * 
     * @param mc
     * @return アクション
     */
    private String getAction(SOAPMessageContext mc) {
        String action = null;
        try {
            String endpointAddress = (String) mc
                    .get(BindingProvider.SOAPACTION_URI_PROPERTY);
            String actionUri = (new URL(endpointAddress)).getPath();
            String tokens[] = actionUri.split("/");
            action = tokens[tokens.length - 1];
        } catch (Exception e) {
            new RuntimeException(e);
        }
        return action;
    }

    /**
     * UTCによるタイムスタンプ(ISO8601フォーマット)の生成
     * 
     * @return UTCによるタイムスタンプ(ISO8601フォーマット)
     */
    private String getTimestamp() {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat is08601 = new SimpleDateFormat(
                "yyyy-MM-dd'T'HH:mm:ss'Z'");

        is08601.setTimeZone(TimeZone.getTimeZone("UTC"));
        return is08601.format(calendar.getTime());
    }

    /**
     * シグネチャ(ハッシュ値)の計算 アクションとタイムスタンプを連結した文字列を元にハッシュ値を計算する。
     * 
     * @param action
     *            実行するアクション
     * @param timestamp
     *            UTCによるタイムスタンプ(ISO8601フォーマット)
     * @return シグネチャ(ハッシュ値)
     * @throws Exception
     *             If there were errors or missing, required classes when trying
     *             to calculate the hash.
     */
    private String calculateSignature(String action, String timestamp) {
        String toSign = (action + timestamp);

        byte[] sigBytes = mac.doFinal(toSign.getBytes());
        return new String(Base64.encodeBase64(sigBytes));
    }

}


AWSECommerceServiceProxyHandlerの説明
70~121行目
ハッシュ情報のヘッダへの埋め込みを行う部分。本実装では72~75行目でアウトバウンド(リクエストメッセージ)以外は無視するようにしている。77~84行目ではヘッダに埋め込むためのアクション、タイムスタンプ、ハッシュ値を取得し、86~118行目で取得した値のヘッダへの埋め込みを行う。
125~127行目
ヘッダはhandleMessage()メソッドで動的な内容を含め設定するので、ここでは一旦空のヘッダを返している。
132~151行目
AWS アクセスキー識別子、AWS 秘密キーの設定と、それらを元にしたハッシュ計算用の秘密鍵を設定している。ここで自分のAWS アクセスキー識別子、AWS 秘密キーの設定をする。また、AWS アクセスキー識別子、AWS 秘密キーは外出しにして、設定ファイル等から取得するようにしてもよい。
159~171行目
エンドポイントのURLから実行するアクション(エンドポイントのURLの一番最後の階層)を取得する。
178~185行目
現在時刻(UTC)からタイムスタンプの文字列(ISO8601フォーマット)を生成する。

(3)実装したSOAPハンドラ(AWSECommerceServiceProxyHandler)をハンドラ・チェーンに設定する

ハンドラ・チェーンの設定ファイルの作成
(2)で実装したAWSECommerceServiceProxyHandlerをハンドラ・チェーンに設定するために、パッケージ内にハンドラ・チェーンの設定ファイル(AWSECommerceService-HandlerChain.xml)を作成(下記参照)し、パッケージ(com.amazon.webservices.awsecommerceservice._2011_08_01)のフォルダ内に配置する。

<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
    <handler-chain>
        <handler>
            <handler-name>proxyHandler</handler-name>
            <handler-class>com.amazon.webservices.awsecommerceservice._2011_08_01.AWSECommerceServiceProxyHandler</handler-class>
        </handler>
    </handler-chain>
</handler-chains>

ハンドラ・チェーンの設定
作成したハンドラ・チェーンの設定ファイルを実装に組み込むために、(1)で生成したProduct Advertising APIのAWSECommerceServiceクラスのAWSECommerceService()メソッドにハンドラ・チェーンのアノテーションを追加する(下記参照)。

@WebServiceClient(name = "AWSECommerceService", targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01", wsdlLocation = "http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl")
public class AWSECommerceService
    extends Service

↓

@WebServiceClient(name = "AWSECommerceService", targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01", wsdlLocation = "http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl")
@HandlerChain(file="AWSECommerceService-HandlerChain.xml") ←この行を追加する。
public class AWSECommerceService
    extends Service


以上で機能的な部分の実装は完了。

(4)Product Advertising APIを呼び出す

ここでは単純にmain()メソッドからProduct Advertising APIを実行するサンプルを実装する。

import java.util.ArrayList;
import java.util.List;

import javax.xml.ws.Holder;
import javax.xml.ws.WebServiceRef;

import com.amazon.webservices.awsecommerceservice._2011_08_01.AWSECommerceService;
import com.amazon.webservices.awsecommerceservice._2011_08_01.AWSECommerceServicePortType;
import com.amazon.webservices.awsecommerceservice._2011_08_01.Item;
import com.amazon.webservices.awsecommerceservice._2011_08_01.ItemSearchRequest;
import com.amazon.webservices.awsecommerceservice._2011_08_01.Items;
import com.amazon.webservices.awsecommerceservice._2011_08_01.OperationRequest;

public class AmazonService {

    @WebServiceRef
    private static AWSECommerceService AWSECommerceService;

    /** AWS アクセスキー識別子 */
    private static String awsAccessKeyId = "xxxxxxxxxxxxxxxxxxxx";
    /** AmazonアソシエイトのアカウントID */
    private static String associateTag = "XXXXXXXXXX-nn";
    /** 共通のリクエストパラメータ(Validate) */
    private static String validate = "False";
    /** 共通のリクエストパラメータ(XMLEscaping) */
    private static String xmlEscaping = "Single";

    public static void main(String[] args) {

        // サービスのエンドポイントの取得
        AWSECommerceService = new AWSECommerceService();
        AWSECommerceServicePortType endPoint = AWSECommerceService
                .getAWSECommerceServicePortJP();

        // 変数の初期化
        String marketplaceDomain = null;
        ItemSearchRequest shared = null;
        List<ItemSearchRequest> request = new ArrayList<ItemSearchRequest>();
        Holder<OperationRequest> operationRequest = new Holder<OperationRequest>();
        Holder<List<Items>> items = new Holder<List<Items>>();

        // ItemSearch用のリクエストオブジェクトの生成
        ItemSearchRequest itemSearchRequest = new ItemSearchRequest();
        itemSearchRequest.setKeywords("新居昭乃");
        itemSearchRequest.setAvailability("Available");
        itemSearchRequest.setSearchIndex("Blended");
        itemSearchRequest.getResponseGroup().add("Medium");

        itemSearchRequest.getResponseGroup().add("ItemAttributes");
        request.add(itemSearchRequest);

        // サービス(itemSearch)の実行
        endPoint.itemSearch(marketplaceDomain, awsAccessKeyId, associateTag,
                xmlEscaping, validate, shared, request, operationRequest, items);

        // 検索結果の表示
        for (Item item : items.value.get(0).getItem()) {
            System.out.println(item.getItemAttributes().getTitle());
            System.out.println("\t" + item.getDetailPageURL());
        }
    }
}

19~26行目
AWSアクセス識別子、amazonアソシエイトIDなど必要な初期値を設定する。(認証はヘッダ情報で処理されるため、ここ設定されるAWSアクセス識別子は実際見ていない模様。また、共通のリクエストパラメータの設定が効いてないかも、Validateの設定を変えても結果が変わらなかった。この点はちょっと不明)
30~33行目
(1)で生成したサービスクラスから、エンドポイント(ここでは日本サイト)を取得する。
35~50行目
itemSearchを呼び出すのに必要なオブジェクトを初期化・設定する。設定する内容はProduct Advertising APIを参照。
52~55行目
itemSearchの実行
56~60行目
検索結果を表示
これで、WS-Securityを利用しない方法で実行が可能。
実際のSOAP呼び出しの際、SOAPメッセージに必要な要素がない場合、だいたい400:Bad Requestとなり、IDなどの認証情報に誤りがある場合403:Forbiddenとなるようだ。

2014年1月11日土曜日

JavaScriptによる関数のラッピング

目的

既存のWebアプリケーションにおいて、可能な限り既存の処理を変更せず関数の呼び出し前後に処理を入れたいことがある時の方法を考えてみた。

適用例

この方法の適用例としては以下のような状況が考えられる。

  • デバックやトレース情報の採取や表示を行う。
  • フォームのonSubmitやonCheckなどのイベントに既に関数を割り当ているときに処理を追加する。
実装方法

JavaScriptには、関数の this 値を指定のオブジェクトで置き換え、関数の引数を配列を置き換えて関数を呼び出詩を行うapplyメソッドという関数が用意されているのでこれを利用し実装する。
実際のコードとしては、既存の関数の前処理、後処理を実装した関数(functionWrapper)を用意し、既存関数の呼び出し部分を変更することにより、functionWrapper内に定義された処理を元の関数の呼び出し前後に処理を挟み込むことができる。
/**
 * 引数で指定した関数をラッピングしての前後に
 * 処理を差し込みます。
 * @param functionName 関数名
 * @return {Boolean}
 */
function functionWrapper(functionName){

    //前処理
    ~

    //既存ロジックの呼び出し
    var tmpArguments = 
           new Array(arguments.length-1);
    for(i = 0;i < arguments.length-1;i++){
        tmpArguments[i] = arguments[i+1];
    }
    var retValue = window[functionName]
                     .apply(window,tmpArguments);

    //後処理
    ~

    return retValue;
}

既存関数の呼び出しとして、例えば以下のようにformのonSubmitに既存のフォームのチェックをするcheckFormという関数の前後になんらかの処理を入れる場合、もともと定義されているchekFormという関数を文字列としてfunctionWrapperの第一引数とし、もともとcheckFormに指定されていた引数をそのままfunctionWrapperの第二引数以降に指定し、functionWrapperの呼び出しに書き換える。 (関数呼び出しの際の引数の受け渡しは、JavaScriptではオーバーロードがサポートされていないため同じ関数名は同一関数が実行されすべての引数がわたされるという性質を利用している。) この書き換えによりfunctionWrapper内に定義された処理を元の関数の呼び出し前後に処理が差し込まれる。
<form method="GET" action="action.do" 
  onSubmit="return checkForm(this);" >
<input text="name" value="value" />
<input text="submit" />
</form>

↓

<form method="GET" action="action.do"
  onSubmit=
    "return functionWrapper('checkForm',this);">
<input text="name" value="value" />
<input text="submit" />
</form>


※2014/02/09 ソースコードの表示方法を変えました。


2013年5月11日土曜日

Philipsのノンフライヤーを購入したのでいろいろ試してみた


揚げ物は油の処理が面倒なので絶対に自宅では作ることはないと思っていたんだけど、空気と熱で揚げ物を作るという“油を使わない揚げ物調理器”であるPhilipsのノンフライヤーが日本で発売されたので早速購入しいろいろと試してみた。総評としては、お手軽にヘルシーな揚げ物的なものを作るには良いものだといったところか。
下の写真を見ればわかると思うが、食材を調理する部分はバスケットとバスケットパンに分けることができ、それぞれ食器と同様洗うことができ、お手入れは簡単。使用するときはバスケットパンにバスケットをいれ、バスケットの網の部分に食材を置いて調理する。バスケットパンとバスケットの間に隙間があり、ここを空気が流れる。


取りあえずいろいろ試した結果、つぎの良い点・悪い点が分かった。
<良い点>
  • 後片付けが比較的楽、バスケットとバスケットパンは食器と同様に洗える(食洗器もOK)
  • 基本的に調理するために油は全く必要としない。
  • 油分が抑えられヘルシー(ただし、特別美味しくできるというわけでもない)
  • 最適な調理時間さえ押さえれば、常に同じ仕上がりが期待できる
<悪い点>
  • 消費電力が大きい(最大1420W)。ただし、常時この電力を使うというわけではなく温度が保たれている間はヒーターが自動で切れる。
  • 1度に調理できる量が少ない。とんかつはせいぜい2枚程度。
  • ちょっと音がうるさい(感覚的には掃除機のナイトモードぐらい?)
  • 調理中の食材が見えないため、最適な調理時間の見極めが難しい。
  • かつなど衣があるものは、ムラができてしまう。(見た目を美しく仕上げるには向かない)
以下いろいろ試した内容を記述する。

(1)フライドポテト

まずは、冷凍食品のポテトを使用してフライドポテトを作ってみた。
見た目・味ともに油で揚げたフライドポテトに近い感じに仕上がった。しかも、油っぽくなくていくらでも食べられる感じ。

(2)から揚げ

鶏もも肉に市販のから揚げ粉をまぶし、ノンフライヤーで調理する。

調理後、バスケットパンに余分な油分が落ちていた。これだけでも油分が減っていることがわかる。

もともと鶏肉に含まれていた油分があり、意外とジューシーな仕上がり。皮もパリパリ。ただ、一部ムラ(粉っぽく見えるところ)があった。熱は通っているので食べる分には問題なし。

(3)とんかつ

豚ロースに衣をつけ、ノンフライヤーで調理する。1度に作れるとんかつは2枚がせいぜい。

周りはきつね色になるが、全体的にきつね色にするのは困難。何度か挑戦したが、どうしてもムラが残り、きれいなきつね色にはならなかった。ネットで確認したが、揚げムラ?に悩まされている人は多いようだった。見た目きれいなとんかつを作るのはかなりの難度と思われる。

ただし、しっかり中まで加熱されていることは間違いない。

(4)ステーキ

あと、ステーキも作ったんだけど、写真撮り忘れたみたい。ただ、ステーキはフライパンで作るのとそれほど変わらない気がする。

2013年1月19日土曜日

アーロンチェアにヘッドレストをつけたよ!!

ワーキングチェアとして長年愛用してきたアーロンチェア。ヘッドレストがないことがずっと不満だったけど、つい最近サードパーティーからヘッドレストが販売されていることを知り、速攻購入し装着してみた。
 ちなみに、アーロンチェアとは、ハーマンミラー社が販売している割と有名なワーキングチェアで、コンセプト通り作業するために特化した椅子である。あくまで作業するための椅子なので、ハーマンミラー社としてはアーロンチェアにはヘッドレスト的なものは必要なしということらしい。 (ただ、作業していれば、椅子の上でちょっとくらい休みたくなるよね!!)

ということで、以下アーロンチェアの気に入っている点およびヘッドレストの使用感を述べてみたい。

アーロンチェアを個人的に気に入ってる点は以下の通りで、とにかく座っていて疲れにくい。
  • 全体的にメッシュ構造で夏でも蒸れない。また、メッシュの絶妙な反発で接地面(太ももやおしり)に圧迫感をあまり感じない。
  • リクライニング時の反動も絶妙で宙に浮いている感じがする。
    (これは実際に座ってみないとわからない感覚だと思う。)
  • リクライニングを前に傾け、前傾姿勢をとることができる。
    (前傾姿勢をとると前のめりに緊張が増し、やる気が増すように感じる)
  • 作りがシンプルで構造がしっかりしているため長持ちする
    (10年近く使用しているが、不具合らしい不具合はない)
気に入っている点は以上になるが、1つ注意点がある。アーロンチェアについているキャスターは意外と硬いのでフローリングで使用する際は、厚手のカーペットまたはマットを引くことをお勧めする。ないと確実にフローリングにダメージ(キャスターのあと)が残ることになると思う。

そして、ヘッドレストの使用感は以下の通り。
  • 付属の6角レンチでネジを締めて固定する仕組みになっていて、かなりしっかりと固定される(ぐらつきは全くない)。そのため、都度装着・脱着するようなことには向いていない。基本的に使用しないときは下図のようにヘッドレスト部分の角度を変えることになる。
  • ヘッドレスト部分のメッシュは、本体のメッシュの素材は異なっているようで、本体のメッシュより張りが少しゆるい。また、色味も少し異なっている(若干艶っぽい感じで若干安っぽく見える)。
    ただ、それほど気にはならないとは思う。
  • ヘッドレストに頭を預けリクライニングすると、背中のラインとヘッドレストの位置に若干の違和感を感じる。長時間休むには向いていないと思う。(サードパーティーが作成したものでこの辺はしょうがないか)
    ただ、疲れた時にちょっと頭を預けるには十分か。
総合的にみると5点満点中3.8点というところだけど、今までずっとなかったことを考えると、やはり必須アイテムと思う。


2012年7月7日土曜日

B&W(Bowers & Wilkins) CM5 購入しました。

先日、スピーカー新調してからおよそ一週間たちエージングもそこそこ進んだ感じなので感想などを書いてみる。

B&W CM5(写真上)を購入する前は、DIATONE DS-600ZA(写真下)を使用していました。DS-600ZAもかなり気に入っていた良かったのですが、少し音の立ち上がりが遅くキーボードなどを繋げると音のアタック部分が若干消えてしまうところだけが不満でした。そこで今回は、なるべく音の立ち上がりがクリアな音を求め、予算と相談した結果CM5に決定し購入しました。
ウーハーとか買うつもりがないので、ホントはもう少し大きなクロージャーのスピーカーが良かったんだけど、DS-600ZA位の大きさのスピーカーが少なくなってきたのと、トールボーイ型ではちょっと部屋に置くスペースを確保するのが難しいという点もCM5にした理由の一つ。
自分自身オーディオマニアというわけでもなく、そんなにいろんなスピーカーの音を聴いているわけでもないのため、感想は前に使用していたDS-6000ZAとの比較っぽい点があると思いますがご容赦願います。

CM5の視聴環境

オーディオマニアではなけれど、今回はケーブルなど可能な範囲でいろいろ頑張ってみました。
  • スピーカー Bowers & Wilkins CM5(色はローズナット)
    今回購入したスピーカー
  • アンプ Pionneer VSA-920TV、ゲーム機、楽器などを色々なソースを接続するために、以前購入したAVアンプ。
  • ケーブル BELDEN 1810Aスピーカーと一緒に新調した4芯のケーブル。思ったより柔らかく引き回しが楽でした。
  • インシュレーター audio-technica ハネナイトAVインシュレーター

まずは、アンプとスピーカーとの接続方法ですが、一般的な接続方法として大きく分けると3パターンあります。

  • Single-wire(シングルワイヤ)
    一般的な接続方法。アンプの±とスピーカーの±をそれぞれ1本のケーブルで接続する。
  • Bi-wire(バイワイヤ)スピーカー側のLHの±とアンプの±を4本のケーブルを使用して接続する  。アンプ側の±は1つで、それぞれ2本接続する下図(右側) 。(Bi-wire接続する場合はブリッジ金具を外して接続する。)
    ※Bi-wire接続することでスピーカーの高音域と低音域の干渉を少なくするらしい。
  • Bi-amp(バイアンプ)アンプ側の対応が必要となるが、Bi-wireをパワーアップした感じで、アンプ側の±がそれおれ独立した回路から接続する形。アンプ側の回路も別になるので更に干渉が少なくなる。
    (VSA-920にはゾーン2(メインとは別の機器を接続するための回路)をBi-amp用の回路として使用するモードがある)

ホントは、Bi-ampで接続したかったんだけど、ゾーン2を別室用のアンプと使用しているため、Bi-ampはあきらめ、今回はBi-wire接続することにしました。スピーカー側の接続は左の写真の様な感じになります。

CM5の感想

まずは、エージングしつつ感じた全体的なイメージ、
  • ファーストインプレッション(購入直後)
    高音がキツく、音が軽い。ただ、中高音の鮮明さは流石。
  • 2日後(エージング15時間位経った頃)
    高音のきつさが少しとれ、全体的にまとまってきたけどやっぱり低音が軽い。
  • 1週間後(エージング30時間以上経った頃)
    中高音と低音のバランスが良くなり、全体的なレベルが数段上った感じになった。ただ、やっぱり低音はやっぱり少し軽い感じがする(これは以前使用していたスピーカーの低音がかなり強いためだと思われる)。
このスピーカーは、エージングによってかなり感じが変わるようです。特に購入直後の音は、お店で聴いたのと全く違い、何か接続を間違えたかと思うレベルです。
エージングが進むと全体的な音のバランスが良くなり、まとまり感が増してくるようです。中高音は音の立ち上がり位も早く、鮮明な音が素晴らしいです。ただ、ある程度の音量で鳴らさないと低音がほんと弱い感じです。低音が弱く感じた場合、少し音量を上げてみると良いかもしれません。
音質は素直な感じで、アンプ側のエフェクトによく反応してくれます(というか効き過ぎな感じがしてすぐに戻してしまうのですが。。。)


空間的な感じはしないので、映画などにはあまり向かないかもしれません。(ただ、アンプ側である程度カーバー可能?)。また、低音の「ず~ん」という感じは、あまりでなっく映画を視聴するにはウーハーが別にないと物足りない。
ハイハットやシンバルのような音は残響が少し耳に残ります。


最後に楽器種別毎の感想

  • 弦楽器系(ストリング系)高音域は少しきつい感じがするけど中低音から高音域の間は、響きが素晴らしい。スピーカー本体が単板で作成されているという事前知識によるプラシーボ効果もあり、スピーカーが共鳴しているような感じすらする。
  • 鍵盤系(ピアノなど)
    音の粒が鮮明でとても心地良い。
  • 管楽器系特にないというか、全体的に高レベルな音であるのは間違く、突っ込みどころがない。
  • ドラム関係
    ハイハットなどの残響が少々耳障りな感じ。音の粒が細かいせいか、ドラムの音が軽い感じになる。
  • ボーカル音は鮮明で前に出てくる感じ。ただ、ボーカルが際立ちすぎるのか、他の音とあまり馴染まない感じで聴こえる(極端に云うとバックの演奏と独立した感じで聴こえる)

2011年9月17日土曜日

狭い部屋でKinectを使えるようにするZOOM for Xbox 360

Xbox 360 Kinect センサーを使うためにはそれなりの広さが必要(Kinectから1人だと1.8m、2人だと2.5m離れないと正しく認識されない)なため、買ったは良いけど部屋が狭くお蔵入りとなっていた。しかし、この距離の問題を解決してくれるZOOM for Xbox 360が発売されたので早速購入してみた。パッケージや商品のデザインは、純正品っぽい感じを出している。




仕組みとしては、焦点距離の短いレンズを本体に被せる形で装着して識別出来る範囲を近くするようだ。商品説明では40%程度近づくことが可能とある。装着もワンタッチで簡単に装着できる。装着した感じもあまり違和感がない感じに仕上がっている。


装着後、早速Kinectの設定を行ってみると、1.4mほどの位置で無事認識することができた。40%ちかづけるというのもホントだった。



とりあえず、ゲーム(ユアシェイプ フィットネス・エボルブ)をやってみた。他のレビューなどを見ると認識率が悪くなるようなことも書かれているのでちょっと気になるところもあるが、とりあえず遊べそうだ。ゲーム画面の右側に映っているのが自分なんだけど、Kinectって着ているものも含め結構細かく認識するんだな~と改めて関心。



Kinect SDKも公開されたし、これでKinectで色々と遊べそう。



2011年9月4日日曜日

getResourceAsStream()でリソースが読み込めない

getResource(),getResourceAsStream()でちょっとはまったので忘れないうちにメモっとく。
まずは動作の基本から、クラスローダ(ClassLoader)に対するgetResource(),getResourceAsStream()は、クラスパスのルートのパスを検索するようになっており、引数で渡した文字列でそのままクラスパスのルートから検索する。なので、文字列の頭に基本的に"/"は付けない。
一方、クラス(Class)に対するgetResource(),getResourceAsStream()は、引き数の文字列の頭に"/"が付いてる場合は"/"を以降の文字列で、引数の文字列の頭に"/"がついていない場合はパッケージ名の"."を"/"に置き換え引数の文字列と"/"とで連結した文字列を、クラスパスのルートから検索する。
例えば、foo.bar.TestMainというクラスがあり、同じパッケージのフォルダ内にTestMain.propertiesというリソースがあった場合、以下の3つの方法でアクセスが出来る(すべて同じ動作)。


TestMain.class.getClassLoader()
    .getResource("foo/bar/test.properties"));

TestMain.class
    .getResource("/foo/bar/test.properties"));

TestMain.class
    .getResource("test.properties"));

ここまでは、位置に依存しない方法でのリソースへのアクセスの仕様を見ればわかるのだけれど、実行時にクラスがjarファイルなどに格納されている場合、実行環境によりなぜかgetResourceAsStream()でリソースが読み込めない場合があるようだ。どうやら、内部的にファイルとしてアクセスしようとして、zip内のファイルを読み取ることが出来ずに例外が発生しているらしい。ちなみにgetResource()でURLはちゃんと戻るからファイルの存在は認識しているようなので不思議だ。
今回、Windowsで開発していたときは問題なかったのだが、LinuxのWebLogic上で実行した際に、リソースにアクセス出来ないという現象が発生していた(WebLogicはwarをデプロイすると/WEB-INF/classesがjarにまとめられる)。


結局のところ実行環境によらずにリソースにアクセスするための解決方法としては、下記のようにするのが良いようだ。
URL resource
    = new URL(TestMain.class.getResource("."),
              "test.properties");

InputStream in 
    = resource
      .openConnection().getInputStream();
:


※2014/02/09 ソースコードの表示方法を変えました。




2011年3月10日木曜日

LifeTouch NOTEを使ってみる

以前モバイルギアを使用していたこともあり、モバイルギアの後継とも思えるLifeTouchノートを早速購入したので使用感などを報告しようと思う。

まず、本体の色だが、購入する前は黒または赤にしようと考えていた。しかし、実物を見ると黒はツヤがありすぎて指紋が目立ちそうだし、赤はちょっとイメージしていた感じと違っていた。意外にチョコレートブラウンの色がいい感じだったのでチョコレートブラウンを選択することにした。

モバイルギアと比較するために、並べて写真を撮ってみた。横幅が少し短くなっているが大きさはほぼ同じくらいで薄くなっている。

初回起動時にネットワークの設定が必須(ウィザードに従うとネットワーク有効にならないと先に進めない)のようなので注意が必要かも。。。


起動は、30秒ちょっとと思ったより時間がかかる感じ(動画参照)。ただ、スリープからの復帰は瞬時なので普段は気にならないと思う。文字の入力する際のキータッチはモバイルギアよりちょっと浅めでちょっとちゃちな感じするが、それほど悪くはない?少なくともASUSのeeePC901よりは入力しやすいと思う。。また、モバイルギア同様に、IMEとしてATOKが入っているのがうれしい。難点をあげると、_(アンダーバー)キーの位置か、なにゆえにカーソルを挟んでしまったかな。。。。

ついでにモバイルギアのスタイラスとLifeTouch NOTEのスタイラスも比べてみた。やっぱり、スタイラスはモバイルギアのほうが大きくて使いやすいと思うが、本体の厚みがちがうので仕方が無いところか。。


ちなみに、Andoroid OSのバージョンは2.2。バージョンアップが提供されるかが気になるところ。しかし、Android Marketがインストールされているところが良い点。また、Android Marketの他に、NECのAndronaviもインストールされているので、追加でアプリケーションをインストールしたいときに困ることはなさそう。

早速ツイッターなどのアプリを入れてみた。動作は軽快だがちょっとフォントが見づらいかも。。。

まだ、触ったばかりなのでもう少し慣れてから続報をお届けしたいと思う(いつになるかは不明)



2011.09.17 更新
今更なのだが、、_(アンダーバー)キーの位置とかをカスタマイズ出来るアップデートが公開されている。「Insert」・「カタカナ」・「←」・「↑」(矢印キー)「ろ」の5つのキーで配置パターンを選べるみたい。ついでにキートップもなんとかなると更に良いのだけどね。。。
「個人向けスマートブック「LifeTouch NOTE」無償アップデートを開始 」
http://121ware.com/navigate/support/ltn/info/20110627/

2009年11月1日日曜日

WindowsでAppleのMagic Mouseを使用する



Apple からMagic Mouseが発売されました。Magic Mouseが発表されたとき、一目見たときからそのデザインのよさにほれ込んでしまい、Apple Storeで発売されるとすぐに購入してしまいました。実際に届いたマウスは右側の写真です。化粧箱もなかなかかっこいい。ちなみに右側のマウスは、携帯用のお気に入りマウスのMicrosoft Arc Mouseです。
基本的にはMagbookをもっているのでそちらで使うつもりで購入しました。しかし、Windowsでも使えないかとためしたところ、専用のドライバがないためタッチによる操作、スワイプはもちろんのことホイール操作もできませんが、Bluetooth接続で普通の2ボタンマウスとしては使用できることがわかりました。
ちょっとしたステータスとして持ち歩く分にはかっこいいマウスであることは間違いないのでWindowsでの設定方法を紹介します。
ここでの設定は、EeePC901(windowsXP SP3)との接続ですの例ですが、実績としては、WindowsXP SP2とPLANEX BT-MicroEDR2Xとも接続できたので、Bluetoothのデバイスさえあればたいていの場合接続できると思います。






Magic Mouse側の電源をONにして、マイ Bluetoothを開きます。そうするとMagic MouseがApple Wireless Mouseとして認識されます。






次に認識されたApple Wireless Mouseを選択し、コンテキストメニューから「デバイスを接続」選択します。




HID(Human Interface Device)に接続するには[はい]をクリックししてください。とたずねられるので「はい」を選択します。






デバイスの接続を選択すると接続確認をもとめられるので、表示されている時間内に「ここをクリック」をクリックして接続を完了させます。







デバイスが接続状態となると同時にセキュリティコード(PIN)の入力を求められます。ここには、「0000」と入力することで接続できます。最初ここで接続できなくてはまり結構悩みました。ちなみにWindowsXP SP2では、端末側のセキュリティコードの設定を聞かれますが、端末側は設定なしでOKでした。



以上の設定でMagic MouseをWindowsで使用することができます。ただし、先ほど述べたようにスワイプ等ができないので、残念ながら見た目を自慢することくらいしかできませんが。。。。




2009年8月13日木曜日

スリープトラッカーで快適な目覚めを[SleeptrackerPRO ELITE]


「ほぼ目覚めている状態」にアラームが鳴り、起こしてくれるという画期的な?腕時計です。睡眠には、眠りの浅い状態のレム睡眠と眠りの深い状態のノンレム睡眠の2種類の状態があります。このスリートラッカーは、眠りの浅い状態のレム睡眠の半覚醒状態に近いときに、アラームが鳴り起こしてくれるというものです。また、眠りの浅い状態を記録し、それをUSB経由で付属のWindows用のソフトウェアで管理することもできます。

まず、目覚ましの機能ですが、仕組み的には時計に加速度計が内蔵されおり、寝ている時の動きによって眠りの状態を判断しているようです。実際に使用した感覚では、私の場合は寝返りのタイミングでアラームが鳴るといった感じでした。ここら辺の判定は、個人差はあると思います。この時計のうたい文句であるすっきりとした目覚めであるかは微妙ですが、普通の目覚ましよりは、気がつきやすいのではないかと思います。
それから、アラームの鳴る時間は、アラーム時間とアラーム・ウィンドウ(分数)で時間帯として設定します。設定したアラーム時間よりアラーム・ウィンドウ(分数)前の間の眠りの浅い時にアラームが鳴ります。例えば7時にアラームを設定し、アラーム・ウィンドウを60分と設定すると6時から7時の間で眠りの浅い時にアラームが鳴ります。設定した時間帯に眠りの浅い状態がなかった場合でも、アラームの設定時間にはアラームが鳴ります。ちなみに、アラーム・ウィンドウ(分数)は、最大90分の設定が可能です。
また、スリープトラッカーPROシリーズでは、アラームの他にバイブレーションも選択することができるので、音が聞こえない状態でのアラーム機能としても使えます。例えば、音楽を聴きながら長時間電車に乗っているときに、時間を知らせてほしい場合とか。

次に、データを管理するソフトです。スリープトラッカーでは1日分の睡眠データが記録されるので、このデータを毎日USB経由で転送します。データ転送し、睡眠データを登録するとき(左図)に、自分の気分と状態(昼寝をしたとか、アルコールを摂取したとか)を登録することで、どういったことが睡眠に影響しているのかを記録していくことができます。

登録したデータは、画面(下図)で確認できます。左側のバー表示が眠りの状態で、黒丸が付いている部分が眠りの浅かった時間になります。この画面でいうと8月13日のAM2:30頃~AM4:00過ぎまでは、眠りが深かったことを示しています。また、8月11は、黒丸が多いのでかなり眠りが浅かったということになります。
そして、右側には、先ほどの画面で登録した気分と状態がアイコンで表示されます。これをもとに自分の睡眠の良い状態を維持・管理していくといったことになります。



まあまあ実用性ある時計だと思いますが、次の点は改善してほしいなと思っています。(ただ、すでに購入しているので改善されたら、改善されたでくやしいだけなのですが。。。。)
  • アラームの種類・音量はバリエーションがほしい(アラームと音量は固定となっている)。
  • デザインをもう少しなんとかほしい。(個人的にはあまり気にっていない)。
  • USB接続でなく、無線(Bluetoothとか)にしてほしい(クリップで挟むだけなのでそれほど手間にはなっていないけど、やはり無線の方が便利)。
  • 値段が高い(もうちょっと安くならないかと思う)。
スリープトラッカーのオフィシャルサイトはこちらです。