2009年10月29日木曜日

DroidのGoogle Maps Navigationはスゴそうだ!

DroidのGPSを使ったGoogle Mapsナビはスーパー・クール!

スーパー・クール、いいですねぇ。記者の興奮が伝わってきます。
実際に、YouTubeにあるGoogleの公式紹介ビデオを見ましたが、これは欲しくなります。
ちなみにビデオの中で彼が着ている「I am here.」というTシャツも欲しくなります(^^)



音声検索ができるし、ストリートビューに自動的に切り替わったりするし、ドックモードもあって、しかも無料。カーナビゲーションシステムを開発しているところは苦しくなるかもな、と心配になるくらいの出来ですね。

恐らく実際に動かしてみると、クラッシュしたり思い通りに動作しないところもあるのでしょうが、チューニングやバグフィックスが重ねられてソフトの品質が高まってくると、脅威でしょうね。


上記の公式紹介ビデオでは、実際に開発に当たっているGoogleのエンジニアの方がソフトの紹介をしていましたが、Google社員はプレゼン能力も高くないといけないんだなと思いました。そういった意味では、Googleはまるで大学の研究室のようでアカデミックですね。

YouTubeのGoogle DeveloppersのチャンネルにはGoogle社員がデモンストレーションやプレゼンテーションをしている多くのビデオがありますが、動画に対するコメントの中にプレゼン能力に対する言及も結構見受けられます。

Googleの社員はエンジニアとしてもプレゼンテーターとしても高い能力が求められるのでしょうね。

2009年10月26日月曜日

空援隊を「知ってしまった」ということ

気迫で実現したご遺骨の帰還:野口 健(アルピニスト)(1)

アルピニストの野口健さんが日本に帰還できていないご遺骨を収集する活動をなさっていることは知っていたが、これほど過酷なものとは知らなかった。というか、活動をしてらっしゃることは知っていても、それがどれほど根気も、精神力も、資金も、政治的な交渉も必要なしんどい活動なのか、その実態を全く知らなかった。登山家なのになんでそのような活動をされてるんだろう、と素朴に疑問に思っていた程度だった。

でも彼の記事を読むと、彼がこの活動を行う決意や衝動のようなものを知ることが出来る。大仰な大義を振りかざすのはなく、正直な言葉で淡々と、しかし力強く。

一番印象に残ったのは彼がなぜこの活動を続けるのかという問に対する答えの部分。


日々いろんな情報や人や出来事に出会う。中には、自分が何か出来ることはないか、考えこんでしまうものもある。でも「知ってしまって」おきながら、自分の背中にはまだそれが背負えないだろうという結論にいたることが多く、罪悪感を感じる時も。

いっそ、知らなければよかったと思ったり、知ることを怖がって新しいものに出会うことを避けようとしたりも。

知ってしまったそのことに正面から向き合い格闘している彼の姿はすごいですね。尊敬します。

自分も彼の活動を知って、何かできることはないか考えて、空援隊に寄付をしました。これからも定期的に行うつもりです。
三菱東京UFJ銀行 京都支店  普通口座 6816051  口座名 トクヒ)クウエンタイ


いろんなものを背負える人間になりたいなぁ~~~!!!!

BlazeではKey型はマッピングされないのでString型をメインに使おう

エンティティグループの永続化をunowned-relationshipで構成しようと、gae.parent-pkを使い、親キーのタイプをKey型で指定していた。

しかし、システムの環境としてBlazeDSを使っていたので、ActionScriptが展開できる型にKey型がなく問題に。ふむー。

ちなみにActionScriptとJavaで互換性のあるデータタイプの一覧はこちら。
Explicitly mapping ActionScript and Java objects

よってKey型をやめ、String型をメインに使うように仕様を変更し、encoded-pk属性を追加。

また、データ登録時に主キーにユニークな値を設定してほしいので、IdGeneratorStrategy.IDENTITYの設定も有りにして、以下のような構成になった。
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Parent{

 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
 private String parentId;

//以下アクセッサー

}

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child {

 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
 private String childId;

 // 親キーIDを格納するプロパティ
 @Persistent
 @Extension(vendorName = "datanucleus", key = "gae.parent-pk", value = "true")
 private String parentId;

//以下アクセッサー

}
これからはBlazeDSとの互換性の意味でも、このような主キー、親キーの設定方法でいこう。


それにしても、Kindの形をドンドン変えられるというのはスゴイ。これまでのRelational DataBaseでは考えられない拡張性だ。フィールドの追加も型の変更も、オンザフライでドンドン変えられる。この体験はまさに別世界だなー。この環境を与えてくれたGoogleに感謝!

2009年10月23日金曜日

オロチのレンタル興味あり!

以前深夜TVで目にしてから気になっていたオロチ。
それがレンタルできるようになっていたそうだ。値段は1泊2日で3万5千円から。
検索・急上昇:光岡 ヒミコ - 毎日jp(毎日新聞)
光岡自動車 ミツオカ・レンタカープラン

不況の影響などで車を売るのが難しくなっている状況を考えると、とてもいいアイデアだと思います。
自分でも今度申し込んでみたいなと(^^)。

ただ、年内は既に予約いっぱいで来年1月からの利用申し込みが11月1日からできるようになるそうです。
(株)光岡自動車 『大蛇』(オロチ)、『卑弥呼』(ヒミコ)レンタカー予約状況について(PDF)

国産のスポーツカーを赤字覚悟で出す!という社長の覚悟で作られたとデザイナーの方がTVでおっしゃってましたが、車も魅力的ですが、それ以上にそういうストーリーに惹かれるのだと思います。

テレ朝のサンデープロジェクトで以前放送された、マツダのRX-8のロータリーエンジンを開発するのに開発者がどんだけ苦労したかとか、日産のフェアレディZの起死回生の話も印象に残っています。

でも、特にこのオロチを出している光岡自動車は富山県富山市の従業員600人未満の会社で、ワンアンドオンリーのものを創っている。その気概に共感します。

オロチのページのFlashもとてもいいです。 大抵こういうものはカッコばかりになりがちですが、そうじゃない。使われている写真の一つ一つに、この車がどんなに素敵か伝えたい!という写真家の愛情を感じます。オロチを十分理解した上で構成されているな、と。神社の境内にオロチを置いて撮影したり、発想もいいですね。

BGMもいい。その名も「大蛇伝説」。マジカッケェー。スクリーンセーバーにしたいくらいです。

頑張れ、光岡自動車!自分もがんばるぞ、と。

gae.parent-pkを使うと親キーの型はKeyかStringでLongはダメ

エンティティグループの永続化をunowned-relationshipで構成していた場合、親キーのタイプはStringかKey型でないとダメだそうだ。

以下のように@Extensionのアノテーションを使って簡単にリレーションを作っていた。
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Parent{

 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 private Long parentId;

//以下アクセッサー

}

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child {

 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 private Key childKey;

 // 親キーIDを格納するプロパティ
 @Persistent
 @Extension(vendorName = "datanucleus", key = "gae.parent-pk", value = "true")
 private Long parentId;

//以下アクセッサー

}
必ずKey型でないといけない~みたいなドキュメントがなかったので、StringかLongでもいけるかなと、この場合はLongを指定していた。そうしたら以下のようなExceptionが出た。
javax.jdo.JDOFatalUserException: Error in meta-data for hoge.Child.parnetId: Parent pk must be of type String or com.google.appengine.api.datastore.Key.
なるほどですね。
StringやKeyはOKだけどLongはNGなのですね。

2009年10月16日金曜日

SDK1.2.6で開発サーバーが起動するようになった

SDK1.2.6で開発サーバーが起動しない問題の続き。

GAE/Jのグループの、SDK1.2.6でも新規に作成したプロジェクトだと問題なく開発サーバーが起動するという投稿を参考にして、新規にプロジェクトを作成した上で、これまでのリソースをコピーし、起動を試してみた。すると、起動するようになった!!やたっ!

[環境]
  • Eclipse : v3.4.2(Ganymade)
  • Google Plugin for Eclipse 3.4 : v1.1.2
  • Google App Engine Java SDK : v1.2.6
注意点は、ユニットテストをするためにappengine-api-stubs.jarとappengine-local-runtime.jarへクラスパスを通している場合。これら2つのjarに関しては、SDK1.2.5に同梱されていたものを使わないといけない。でないと、InvocationTargetExceptionが出て開発サーバーの起動に失敗する。
java.lang.reflect.InvocationTargetException
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:323)
 at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:338)
Caused by: java.lang.RuntimeException: Unable to find appengine-agentimpl.jar in hogehoge\war\WEB-INF\junitlib
 at com.google.appengine.tools.development.agent.AppEngineDevAgent.findAgentImplLib(AppEngineDevAgent.java:97)
 at com.google.appengine.tools.development.agent.AppEngineDevAgent.premain(AppEngineDevAgent.java:48)
 ... 6 more
FATAL ERROR in native method: processing of -javaagent failed
Exception in thread "main" 
要は、ユニットテストのために上記2つのjarをwar\WEB-INF\junitlibに入れ、クラスパスを通していたのだが(ユニットテストの環境設定についてはこちらにまとめています)、1.2.6に同梱されているバージョンのjarを使うと、クラスパスを通しているせいか、起動時にこのjunitlibの中に必要なjar(この場合はappengine-agentimpl.jar)を探しに行ってしまうのだ。

1.2.5同梱バージョンのものを使うと、InvocationTargetExceptionは表示されない。1.2.5のものではなくても、1.2.6のものでなければよいのではないかと思われ。

ちなみに自分の環境だと、-javaangentの指定をVM Argumentsに加えなくても起動します。この辺はEclipseのバージョンとGoogle Pluginのバージョンによるのかもしれません。 とりあえず、1.2.6に移行して上手く行かなくなった場合は、新規にプロジェクトを作り直してマージした方が話が早いということで。

2009年10月15日木曜日

Tweetieで自分のアイコン画像がやっと出た

Twitterクライアントとして、iPhoneでは「Tweetie」を使っている。

近所のユーザー検索などの機能を試したくて購入したが、Twitterクライアントとして機能面でも動作のレスポンス面でもほぼ満足な出来。

ただ気になるのが、自分を含めて何人かのフォロワーのアイコンが表示されないこと。これまでいくつかiPhoneのTwitterクライアントを試してみたが、アイコンが出ないという症状はこれまで経験したことは無かった。

まず疑ったのがサイズ。現在設定しているアイコンの画像サイズは240x240。これを80x80などと小さくしてみたが効果なし。

次に疑ったのがファイル名。

検索してみると、画像のファイル名に日本語が含まれたものをアイコンとして設定するとTweetieでは表示されないという記事を発見。自分の場合、ファイル名に日本語は含まれていないが、数字が含まれているし、数字始まりだ。何かしらファイル名は関係があるかもしれないと思い、アイコン画像のファイル名を全て英字に変更してみた。

すると、出た~!

Tweetie関連で検索してみると、「Tweetie アイコン」という組み合わせが結構上位に出てくる。ということは自分以外にもアイコンが表示されずに困っているユーザーが結構いるのかもしれませんね。Tweetieでアイコンが表示されない方は、画像のファイル名に英字以外が含まれていないかチェックしてみてください。

サプリーンでは数字始まりのファイル名にしているんだが、それだとファイル名をリネームしないとTweetieのアイコンとして表示されないということか・・・。ちょっと問題だなぁ。

ちなみにTweetieはユーザー情報をキャッシュするので、変更したアイコン情報がアプリ側に反映されるまで約1日かかります。効果があったかどうかは1日経ってからチェックする必要があります(^^;)

2009年10月14日水曜日

Eclipse3.4だとSDK1.2.6が問題?

先ほどのSDK1.2.6にして開発サーバーが起動しない問題の調査の続き。
同じような症状に見舞われている方はけーん。
Not able to start development application server anymore in eclipse 3.4 with SDK 1.2.6

Eclipse3.4だと問題なのか?

ちょっとすぐには解決できないかもしれない。優先順位を落として引き続き調査だな。
Eclipseのバージョンアップしかないかもしれない。
ちょっとめんどくさいな。

SDK1.2.6で開発サーバーが起動しない

今日、GAEのSDK1.2.6がリリースされた。PythonとJavaとどちらのランタイムでも出ている。
Google App Engine Blog: App Engine SDK 1.2.6 Released with Incoming Email, App Deletion, and more!

メールの送信だけじゃなく受信もできるようになったり、アプリケーションが削除できるようになったり、ローカルストレージの統計もより細かく見られるようになった。

ためしにEclipseのソフトウェア更新機能でSDK1.2.6をインストールしてアプリケーションを実行したところ、RuntimeExceptionが出て開発サーバーが起動しない。
java.lang.RuntimeException: Unable to locate the App Engine agent. Please use dev_appserver, KickStart,  or set the jvm flag: "-javaagent:<sdk_root>/lib/agent/appengine-agent.jar"
 at com.google.appengine.tools.development.DevAppServerFactory.testAgentIsInstalled(DevAppServerFactory.java:102)
 at com.google.appengine.tools.development.DevAppServerFactory.createDevAppServer(DevAppServerFactory.java:77)
 at com.google.appengine.tools.development.DevAppServerFactory.createDevAppServer(DevAppServerFactory.java:38)
 at com.google.appengine.tools.development.DevAppServerMain$StartAction.apply(DevAppServerMain.java:153)
 at com.google.appengine.tools.util.Parser$ParseResult.applyArgs(Parser.java:48)
 at com.google.appengine.tools.development.DevAppServerMain.<init>(DevAppServerMain.java:113)
 at com.google.appengine.tools.development.DevAppServerMain.main(DevAppServerMain.java:89)
Caused by: java.lang.NoClassDefFoundError: com/google/appengine/tools/development/agent/AppEngineDevAgent
 at com.google.appengine.tools.development.DevAppServerFactory.testAgentIsInstalled(DevAppServerFactory.java:98)
 ... 6 more
Caused by: java.lang.ClassNotFoundException: com.google.appengine.tools.development.agent.AppEngineDevAgent
 at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
 at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
 ... 7 more
これまでどおり、SDK1.2.5に戻すと、正常にサーバーは起動する。

SDK1.2.6のlibの下には、SDK1.2.5のlib下にはなかったagentフォルダがあるが、それが何か関係しているのか?

不思議なのは何度か1.2.5と1.2.6をスイッチングしていると1.2.6でもRuntimeExceptionが表示されなくなるということ。
The server is running at http://localhost:8080/
と出るので、開発サーバーが正常に起動できたのかと思ってしまう。

でも、ブラウザでアドレスにアクセスしてみると、ページは真っ白で、今度はエラーがコンソールに。
java.lang.AbstractMethodError: com.google.appengine.tools.development.DevAppServerImpl.getUserPermissions()Ljava/security/Permissions;
 at com.google.appengine.tools.development.DevAppServerFactory$CustomSecurityManager.checkPermission(DevAppServerFactory.java:128)
 at java.lang.Thread.setContextClassLoader(Thread.java:1351)
 at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:739)
 at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
 at com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:54)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
 at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:342)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
 at org.mortbay.jetty.Server.handle(Server.java:313)
 at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:506)
 at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:830)
 at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:514)
 at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:381)
 at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:396)
 at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442)
2009-10-14 02:19:43.885::WARN:  handle failed
java.lang.IllegalStateException: Request in context!
 at org.mortbay.jetty.Request.recycle(Request.java:163)
 at org.mortbay.jetty.HttpConnection.reset(HttpConnection.java:470)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:450)
 at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:396)
 at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442)
結局、SDKは1.2.5のまま開発を続けていますが・・・
原因は分からず。何なんでしょうねぇ。

2009年10月13日火曜日

よこはま動物園ズーラシアに行ってきた

天気が良かったので、先日ゲットしたPanasonic Lumix DMC-GH1の写真、動画の撮影練習を兼ねて、よこはま動物園ズーラシアに行ってきました。

GH1のレンズキット(GH1K)は、ボケ味が出て、かなり満足なレンズです。さらに、象の肌の質感やペンギンのおなかについている水滴まで描写でき、描写力もすごいです。

2009年10月9日金曜日

Google ストリートビューで宮古島へバーチャル帰省

Googleストリートビューが旭川や沖縄に対応した。
グーグル、ストリートビューの対象エリアを拡大--旭川や沖縄など

ということで、試しにおじぃのおうちまでトリップできるかテストしてみたら、ばっちりおじぃの軽トラまで視認できた。スゴイ。宮古島までバーチャル帰省できた感じ。

だったら勿論、厚木も対応になったんだよねと思って試してみたら、ノーorz。

相模川はまだ越えられていませんでした。うちの周辺はストリートビュー非対応。宮古島以下だなんて・・・

<2010.03.17追記>
相模川を超えたようです。厚木もストリートビュー対応になりました(^^)/

Raul Midonのプレミアムライブに行ってきた

抽選なるものにこれまで当たったことが無いのに、ラッキーにも10月8日に行われるRaul Midonのプレミアムライブに当たっちゃいました!ということで代官山UNITへ。

Raul Midonの出待ち中。
Share photos on twitter with Twitpicニューアルバム「SYNTHESIS」から「Next Generation」「Don't Take It That Way」「Blackbird」など約30分ほど演奏してくれました。やっぱりボーカリストとしてもギターリストとしてもレベルが高かったです。特に「Blackbird」が生で聴きたくて楽しみにしていましたが、鳥が空へ向かって舞い上がっていく開放感が感じられて、よかったです。

でも「おお、いい!」という感じではなく「あー、よかったねぇ」という感じでした。期待が大きすぎたんでしょうか(^^;)


個人的には、Tahnyaのヒラタ君のギターの音の方が好きかも、演奏も彼の方が多才or多彩かも、なんて感じたりして。

改めて、Tahnyaの「Blackbird」もなかなかのもんです。

2009年10月1日木曜日

pk-nameとencoded-pkはセットです

使用されたタグやその使用回数を保存するクラスをつくり、未使用のタグだったら新規追加、既使用だったら回数増加ということをやっていた。タグの文字列はString型でencoded-pk属性。こんな感じ。
@PrimaryKey
@Persistent
@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String tagKey;

@Persistent
private Long count;

public Tag(String tagName) {
 this.tagName = tagName;
 count = new Long(1);
}
タグを追加するときの処理。
for (int i = 0; i < tagArray.length; i++) {
 String tag = tagNameList.get(i);
 Key k = KeyFactory.createKey(Tag.class.getSimpleName(), tag);
 try {
  tagArray[i] = pm.getObjectById(Tag.class, k);
  tagArray[i].increment();
 } catch (JDOObjectNotFoundException e) {
  tagArray[i] = new Tag(KeyFactory.keyToString(k));
 }
}
でも、エンコードされた文字列であるtagNameをデコードして見てみると
Tag(文字列1)
Tag(文字列2)
などのように表示される。うむぅ。文字列だけ取得したいな。

そこでGoogleのドキュメント復習。encoded-pkとpk-nameについて読み込み。
データの作成、取得、削除 - Google App Engine - Google Code

pk-nameにエンコードしていない文字列(つまり表示したい文字列)をセットし保存することにした。1プロパティ追加。
@PrimaryKey
@Persistent
@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String tagKey;

@Persistent
@Extension(vendorName = "datanucleus", key = "gae.pk-name", value = "true")
private String tagName;

@Persistent
private Long count;

public Tag(String tagKey, String tagName) {
 this.tagKey = tagKey;
 this.tagName = tagName;
 count = new Long(1);
}
タグを追加するときはエンコードされたキーとプレーンなStringの両方を引数に与えるようにした。
for (int i = 0; i < tagArray.length; i++) {
 String tag = tagNameList.get(i);
 Key k = KeyFactory.createKey(Tag.class.getSimpleName(), tag);
 try {
  tagArray[i] = pm.getObjectById(Tag.class, k);
  tagArray[i].increment();
 } catch (JDOObjectNotFoundException e) {
  tagArray[i] = new Tag(KeyFactory.keyToString(k), tag);
 }
}
このようにすると、pk-nameを設定したtagNameからプレーンな文字列が取得できるようになった。


と、そこでちょっと考えてみた。

encoded-pkしてStringとして使ってても、結局、文字列を引数にcreateKeyして、keyを文字列へ変換して設定という流れ。ということはKey型の方がシンプルかも?

で、tagKeyの型をStringからKeyへ変更し、tagKeyにつけていた@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true") を削除。タグを追加するときもkeyToString()しない仕様に変えてみた。
@PrimaryKey
@Persistent
private Key tagKey;

@Persistent
@Extension(vendorName = "datanucleus", key = "gae.pk-name", value = "true")
private String tagName;

@Persistent
private Long count;

public Tag(Key tagKey, String tagName) {
 this.tagKey = tagKey;
 this.tagName = tagName;
 count = new Long(1);
}

:
:
:
//タグを追加するとき
for (int i = 0; i < tagArray.length; i++) {
 String tag = tagNameList.get(i);
 Key k = KeyFactory.createKey(Tag.class.getSimpleName(), tag);
 try {
  tagArray[i] = pm.getObjectById(Tag.class, k);
  tagArray[i].increment();
 } catch (JDOObjectNotFoundException e) {
  tagArray[i] = new Tag(k, tag);
 }
}
そしてテストしてみたら・・・

怒られた。

javax.jdo.JDOFatalUserException: Error in meta-data for hoge.Tag.tagName: A field with the "gae.pk-name" extension can only be used in conjunction with an encoded String primary key..

なるほど。pk-name使いたかったらKey型はNGで、String型のフィールドにencoded-pkのExtension付きじゃないとダメですよ、ということなんですね。了解です。