2010年3月30日火曜日

GWT と Flash のどちらをとるか?

以下、個人的なメモです。

opensocial系のアーキテクチャは、以下の前提を持つ
  • トリガーとなる html は、opensocialのコンテナが作成するものであり、基本的に gadget 開発者の制御下にない。
  • トリガーとなる html 内で gadget 開発者の制御下にあるのは、gadget 定義 xml の Content 要素内のみで、これが トリガーとなる html の body 内に配置される。
  • トリガーとなる html 内に iframe で外部サイトを参照する場合、iframe 内からは opensocial 系機能を一切使えない。つまり、トリガーとなる html 内から直接的な関係にある javascript や flash からのみ opensocial 系機能を使用することができる。
  • 外部サーバとの連携には、予め用意された特定の API から以外アクセスすることができない。つまり、独自の RPC 等ラップしないかぎり不可能。


mixi アプリ的制約

iframe等で外部サーバに接続するのは、規約的に許可されない。仮に許可されたとしても、iframe 内からは opensocial系の機能が一切使えない。


PC向けmixiアプリを開発する際の方向性検討

上記のことから、PC向けmixiアプリを作成するのであれば、クライアント側は生の javascript で直接作成か、flash をベースにするのがよさそう。ブラウザのによる動作ブレを考えると、flash のほうがよさそう。javascript が動作する環境であれば、iPhone 以外ならそれなりに動作しそう。品質の視点で考えると、少なくとも向こう1,2年くらいは opensocial 関連で GWT の出る幕はなさそうだし、それ以上たっても可能性は薄いと思われ。


opensocial 系以外の PC 向けサイトを開発する際の方向性検討

特に opensocial 系を意識しないのであれば、GAE/J + GWT で開発するという選択肢がある。インフラの保守コスト低いことや、環境が統一されている点が大きい。safari で動くから iPhone 上でも動作すると思われる。

もうひとつの選択肢として、GAE/J + BlazeDS + flex というのも考えられる。コンテナ側が多少重くなる点とサーバとクライアントの開発環境が密結合していない点は GWT よりも生産性が落ちる要素かもしれない。しかし、クライアント単体の開発速度は flex のほうがかなり高速だと思われる。


今後の方向性

アプリケーションの開発を行う場合、初期にアーキテクチャを決定することがかなり重要である。しかし今回は、どのようなアーキテクチャを採用したとしても、万能なものはできそうにない。技術要素だけではなく、今後の自社の進み方も不明だし、世の中の技術動向で大きく方向が変わってしまう。さてどうしたものか、、、。まぁ、どうせ予想できないんだから、予想できないものは考えないというのが正解だろうなぁ。

確実な点から抑えていくとすると、インフラについては GAE/J と心中することにする。開発の初期メンバ2名でかつフルタイムが1名という現状において、独自インフラの構築や保守の工数は割けない。EC2とか使うと、コストがかかるし、事務手続きだけでもかなり面倒そうだ。

クライアントについては、携帯電話に関しては当面は FlashLite になるだろうから、PC版をGWTで作ろうが Flash で作ろうが影響はない。

opensocial については、GWT よりは flash に軍配が上がりそうな気がする。iPhone に対応しようとしたら GWT のほうがよいかもしれないが、今はそこにこだわるときではなさそうだ。

つまり、flash つかっておけば iPhone 以外には大体対応できるということかな。携帯電話については、フルFlash対応するのが早いか、javascriptに完全対応するのが早いか、よくわからないので、考えても仕方がなさそう。


結論

GAE/J + BLazeDS + Flex で行くことに決定!


直近のTODO

GAE/J + BLazeDS + Flex で mixi アプリをどの程度作れるかは試さないとね。サンシャイン牧場とかあるから大丈夫だとは思いきや、あれって Flex じゃないかも。

mixiアプリのアプリ表示領域のサイズ指定方法

以下、mixi アプリのアプリ描画領域のサイズ変更についてのメモです。


mixi アプリの URL

mixi アプリは、以下のような URL からアクセスされます。

http://mixi.jp/run_appli.pl?id=<アプリのID>


mixi アプリからのデフォルトの画面サイズ指定

上記の mixi アプリの URL から表示されるページでは以下のような iframe タグが指定されていて、その内部にアプリその他が表示されるようになっているようです。

<iframe src="mixiが用意したURL" id="アプリのID" name="アプリのID" height="500" width="945" frameborder="0" border="0"></iframe>

この段階で iframe のサイズが height="500" width="945" と指定されています。


アプリ用 iframe 内部

上記 iframe の先は mixi 側が用意した opensocial 系の API javascript 等を含むページとなっており、最後のほうで、gadget.xml で記述した自サイト用の記述が入るようになっています。


強引にサイズを変更してみる

強引に以下のように親の iframe を変更しようとすると、parent.document の段階でセキュリティに引っかかってしまいます。

<script type="text/javascript">
  alert("in script");
  function resizeAppContentIframe() {
    alert("in resizeAppContentIframe()");
    alert("parent = " + parent);
    alert("parent.document = " + parent.document);
    alert("parent.document.getElementById('app_content_xxxxx') = " + parent.document.getElementById('app_content_xxxxx'));
    var iframeElement = parent.document.getElementById('app_content_xxxxx')
    alert("iframeElement = " + iframeElement);
    iframeElement.style.width = "945px"
    iframeElement.style.height = "800px"
  }
  function init() {
    alert("in init()");
    setTimeout("resizeAppContentIframe()", 10);
  }
  gadgets.util.registerOnLoadHandler(init);
</script>


Gadgets API を使いましょう

結局、以下のように Gadgets API を使えば OK でした。

<?xml version="1.0" encoding="UTF-8"?>

<Module>
  <ModulePrefs title="sample">
    <Require feature="opensocial-0.8" />
    <Require feature="dynamic-height" />
  </ModulePrefs>
  <Content type="html"><![CDATA[

<!-- resize parent iframe -->
<script type="text/javascript">
  gadgets.util.registerOnLoadHandler(function(){ 
    gadgets.window.adjustHeight();
  }); 
</script>

  ]]></Content>
</Module>

以下の2点がポイントです。
<Require feature="dynamic-height" />
gadgets.window.adjustHeight();

親 iframe のサイズを書き換える。

以下のような方法で firefox, IE, chrome で動作するようです。

parent.html
<html>
<body>
<iframe id="parent_iframe" src="./child.html" border="1" width="200" height="200"></iframe>
</body>
</html>


child.html
<html>
<body>
</body>
<script type="text/javascript">
  function resizeAppContentIframe() {
   var iframeElement = parent.document.getElementById('parent_iframe')
  iframeElement.style.width = "945px"
  iframeElement.style.height = "800px"
  alert("iframeElement = " + iframeElement);
  }
  setTimeout("resizeAppContentIframe()", 10);
</script>
</html>

setTimeout を使わないと、chrome では動作しません。

chrome では iframe 内がローカルリソースの場合は親の情報を変更できないっぽい。

下記の parent.html を http ではなくローカルリソースとして読み込んだ場合、chrome では親 iframe のサイズ変更が行われません。

parent.html
<html>
<body>
<iframe id="parent_iframe" src="./child.html"></iframe>
</body>
</html>

child.html
<html>
<body>
</body>
<script type="text/javascript">
  alert("this is child.");
  function resizeAppContentIframe() {
    var iframeElement = parent.document.getElementById('parent_iframe')
    iframeElement.style.width = "945px"
    iframeElement.style.height = "800px"
    alert("iframeElement = " + iframeElement);
  }
  setTimeout("resizeAppContentIframe()", 10);
</script>
</html>

http 経由でアクセスすれば問題なく動作します。

2010年3月27日土曜日

mixiアプリ調査

本家情報ページ
http://developer.mixi.co.jp/appli

まずは、mixiアプリ for PC について読みながら手を動かしてみた。
とりあえず、これだけで一応 mixi アプリを配置して動作確認ができました。

2010年3月26日金曜日

GAE/J + GWT 用アーキテクチャ

GAE/J + GWT 環境について今後議論を進めていくためのベースの環境は以下のように定義する。

議論の出発点となる環境
  • Eclipse + GAE Plugin 環境を用意する。
  • GAE + GWT 環境で新規プロジェクトを生成。
    (仮に com.objectfanatics.gaegwtsample パッケージをベースとして話を進める。)
  • com.objectfanatics.gaegwtsample 以下の全javaファイルを Gaegwtsample.java 以外すべて再帰的に削除。
  • Gaegwtsample.java の onModuleLoad の内部実装をすべて削除して整理。(完全なスケルトンにする)
  • web.xml の web-app 要素内の welcome-file 関連以外を削除。
  • war/WEB-INF/Gaegwtsample.css の内容を1行目のコメント以外削除する。
  • war/WEB-INF/Gaegwtsample.html の title を必要に応じて変更し、h1 要素と table 要素を削除。(仮の title : GAE/J + GWT Sample Project for OBJECT FANATICS)
  • war/WEB-INF/appengine-web.xml の application 要素に GAE の application-id を入れる。

2010年3月24日水曜日

Eclipse上でのXMLファイルのコメント操作

コメントアウトとアンコメントは CTRL+SHIFT+C でできたんですね (^^;

CTRL+/ でできないのであきらめてました、、、。

よく考えてみれば、java の場合はコメントが // だから CTRL+/ なんだろうな。

Google App Engine のインスタンス生存時間

Google App Engine では負荷の状況によってインスタンスの数を自動的に調整してくれるわけですが、数秒アイドルしただけでインスタンスが落とされるのか、数分程度は許されるのか、興味があるところです。

ということで、GAE/J のインスタンスがどの程度保持されるかの簡単な実験をしてみました。
(詳細は付録に参照のこと)

実行結果
アクセスインスタンスの状態
初回初期インスタンス生成
10秒後初期インスタンスのまま
20秒後初期インスタンスのまま
30秒後初期インスタンスのまま
40秒後初期インスタンスのまま
50秒後初期インスタンスのまま
60秒後初期インスタンスのまま
2分後初期インスタンスのまま
3分後初期インスタンスのまま
4分後初期インスタンスのまま
5分後新規インスタンス生成
10分後新規インスタンス生成

上記の結果から、5分という時間が怪しいですね。

ということで、最終アクセスから4分50秒後と5分10秒後のアクセスを何度か試行してみました。

実行結果
アクセスインスタンスの状態
初回初期インスタンス生成
4分50秒後(1回目)初期インスタンスのまま
5分10秒後(1回目)新規インスタンス生成
4分50秒後(2回目)新規インスタンス生成
5分10秒後(2回目)新規インスタンス生成
4分50秒後(3回目)初期インスタンスのまま
5分10秒後(3回目)新規インスタンス生成

微妙な結果になりました。

1回だけですが、最終アクセスから4分50秒後のアクセスでも新規にインスタンスが生成されています。

最終アクセスからインスタンスがシャットダウンされるまでの時間は5分前後かつあまり厳密ではないということでしょうか。

ということで、今度は最終アクセスから4分後と6分秒後のアクセスを10回ずつ試行してみました。

実行結果(4分後)
アクセスインスタンスの状態
4分後(1回目)初期インスタンスのまま
4分後(2回目)初期インスタンスのまま
4分後(3回目)初期インスタンスのまま
4分後(4回目)初期インスタンスのまま
4分後(5回目)新規インスタンス生成
4分後(6回目)初期インスタンスのまま
4分後(7回目)初期インスタンスのまま
4分後(8回目)初期インスタンスのまま
4分後(9回目)初期インスタンスのまま
4分後(10回目)初期インスタンスのまま

実行結果(6分後)
6分後(1回目)初期インスタンスのまま
6分後(2回目)新規インスタンス生成
6分後(3回目)新規インスタンス生成
6分後(4回目)新規インスタンス生成
6分後(5回目)新規インスタンス生成
6分後(6回目)新規インスタンス生成
6分後(7回目)新規インスタンス生成
6分後(8回目)新規インスタンス生成
6分後(9回目)初期インスタンスのまま
6分後(10回目)初期インスタンスのまま

これまた、微妙な結果ですね、、、。

最終アクセス時刻から約4分後の場合と約6分後の場合で、新規インスタンス生成の頻度に明らかな差が出たということは言えると思います。

上記までのデータから言えることは、『約5分間アクセスがないインスタンスはシャットダウンされる可能性が高い』ということでしょうか、、、。


付録

GoogleAppEngine 側の Servlet
public class Initialize_testServlet extends HttpServlet {
  private static final Date initDate = new Date();
  private static String initialDateStr = new SimpleDateFormat("yyyyMMdd hh:mm:ss.SSS").format(initDate);
  private static int count = 1;
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    Date currentDate = new Date();
    String currentDateStr = new SimpleDateFormat("yyyyMMdd hh:mm:ss.SSS").format(currentDate);
    long uptime = currentDate.getTime() - initDate.getTime();
    resp.setContentType("text/plain");
    resp.getWriter().println("Initial Date = " + initialDateStr + "\nCurrent Date = " + currentDateStr + "\nuptime       = " + uptime + "\ncount        = " + (count++));
  }
}

呼び出し側の groovy スクリプト1
int[] intervals = [0, 10, 20, 30, 40, 50, 60, 120, 180, 240, 300, 600]
intervals.each{
  sleep(it * 1000);
  println(new URL("http://.appspot.com/initialize_test").text)
}

実行結果1
Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:10:04.737
uptime       = 1
count        = 1

Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:10:16.141
uptime       = 11405
count        = 2

Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:10:36.676
uptime       = 31940
count        = 3

Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:11:07.177
uptime       = 62441
count        = 4

Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:11:48.297
uptime       = 103561
count        = 5

Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:12:39.432
uptime       = 154696
count        = 6

Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:13:40.550
uptime       = 215814
count        = 7

Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:15:42.093
uptime       = 337357
count        = 8

Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:18:42.732
uptime       = 517996
count        = 9

Initial Date = 20100324 01:10:04.736
Current Date = 20100324 01:22:43.184
uptime       = 758448
count        = 10

Initial Date = 20100324 01:27:45.387
Current Date = 20100324 01:27:45.388
uptime       = 1
count        = 1

Initial Date = 20100324 01:37:47.074
Current Date = 20100324 01:37:47.075
uptime       = 1
count        = 1

呼び出し側の groovy スクリプト2
int[] intervals = [0, 290, 310, 290, 310, 290, 310]
intervals.each{
  sleep(it * 1000);
  println(new URL("http://.appspot.com/initialize_test").text)
}

実行結果2
Initial Date = 20100324 01:45:59.017
Current Date = 20100324 01:45:59.018
uptime       = 1
count        = 1

Initial Date = 20100324 01:45:59.017
Current Date = 20100324 01:50:49.644
uptime       = 290627
count        = 2

Initial Date = 20100324 01:56:02.002
Current Date = 20100324 01:56:02.003
uptime       = 1
count        = 1

Initial Date = 20100324 02:00:53.746
Current Date = 20100324 02:00:53.748
uptime       = 2
count        = 1

Initial Date = 20100324 02:06:06.200
Current Date = 20100324 02:06:06.200
uptime       = 0
count        = 1

Initial Date = 20100324 02:06:06.200
Current Date = 20100324 02:10:57.901
uptime       = 291701
count        = 2

Initial Date = 20100324 02:16:09.484
Current Date = 20100324 02:16:09.485
uptime       = 1
count        = 1

呼び出し側の groovy スクリプト2
int[] intervals = [0, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 360, 360, 360, 360, 360, 360, 360, 360, 360, 360]
intervals.each{
  sleep(it * 1000);
  println(new URL("http://.appspot.com/initialize_test").text)
}

実行結果3
Initial Date = 20100324 04:24:52.316
Current Date = 20100324 04:24:52.317
uptime       = 1
count        = 1

Initial Date = 20100324 04:24:52.316
Current Date = 20100324 04:28:54.485
uptime       = 242169
count        = 2

Initial Date = 20100324 04:24:52.316
Current Date = 20100324 04:32:55.286
uptime       = 482970
count        = 3

Initial Date = 20100324 04:24:52.316
Current Date = 20100324 04:36:55.806
uptime       = 723490
count        = 4

Initial Date = 20100324 04:24:52.316
Current Date = 20100324 04:40:57.665
uptime       = 965349
count        = 5

Initial Date = 20100324 04:45:01.927
Current Date = 20100324 04:45:01.928
uptime       = 1
count        = 1

Initial Date = 20100324 04:45:01.927
Current Date = 20100324 04:49:04.288
uptime       = 242361
count        = 2

Initial Date = 20100324 04:45:01.927
Current Date = 20100324 04:53:06.267
uptime       = 484340
count        = 3

Initial Date = 20100324 04:45:01.927
Current Date = 20100324 04:57:08.374
uptime       = 726447
count        = 4

Initial Date = 20100324 04:45:01.927
Current Date = 20100324 05:01:11.439
uptime       = 969512
count        = 5

Initial Date = 20100324 04:45:01.927
Current Date = 20100324 05:05:13.838
uptime       = 1211911
count        = 6

Initial Date = 20100324 04:45:01.927
Current Date = 20100324 05:11:16.103
uptime       = 1574176
count        = 7

Initial Date = 20100324 05:17:19.835
Current Date = 20100324 05:17:19.836
uptime       = 1
count        = 1

Initial Date = 20100324 05:23:24.557
Current Date = 20100324 05:23:24.559
uptime       = 2
count        = 1

Initial Date = 20100324 05:23:24.557
Current Date = 20100324 05:29:26.754
uptime       = 362197
count        = 2

Initial Date = 20100324 05:35:30.020
Current Date = 20100324 05:35:30.022
uptime       = 2
count        = 1

Initial Date = 20100324 05:41:32.569
Current Date = 20100324 05:41:32.570
uptime       = 1
count        = 1

Initial Date = 20100324 05:47:36.381
Current Date = 20100324 05:47:36.382
uptime       = 1
count        = 1

Initial Date = 20100324 05:53:38.953
Current Date = 20100324 05:53:38.954
uptime       = 1
count        = 1

Initial Date = 20100324 05:53:38.953
Current Date = 20100324 05:59:41.886
uptime       = 362933
count        = 2

Initial Date = 20100324 05:53:38.953
Current Date = 20100324 06:05:43.462
uptime       = 724509
count        = 3

2010年3月20日土曜日

SpeedTracerを入れてみた

Getting Started with Speed Tracer

ここから Dev Channel バージョンの Google Chrome をダウンロードしてインストールする。

--enable-extension-timeline-api オプション付きで Google Chrome を起動する。

2010年3月19日金曜日

SourceForge.net への登録

過去に domaingen という名前で SourceForge.jp に登録をしていたのですが、今回は SourceForge.net に新規プロジェクトを登録してみました。以下はそのときのメモです。


登録方法

Create Project タブから Create Project ボタンを押し、後は登録フォームに以下の情報を記入するだけで手続きは完了です。
  • プロジェクト名
  • プロジェクトのURL
  • プロジェクトの説明
  • 利用規約を読んだよチェックボックスのチェック
あとは、数分待てばプロジェクトの作成が完了します。(今回は1分もかかりませんでした)


ユーザーの設定

ユーザーの設定は、SourceForge.net 全体に対するものであるため、プロジェクトごとに行う必要はありません。

  • 公開鍵の登録
    • このページを参考にして、公開鍵を登録します。
    • 要は、ここをクリックして[Authorized keys: ]に公開鍵を入れるだけです。

SSHやSCP等のインタラクティブな利用

コマンド単発ではなくインタラクティブに操作を行う場合は、interactive shell の作成が必要になります。
  • interactive shell の作成
    • interactive shell を作成します。以下例
      • ssh -t ユーザー名,objectfanatics@shell.sourceforge.net create
      • plink -t ユーザー名,objectfanatics@shell.sourceforge.net create
      • plink -t -i "秘密鍵のパス" ユーザー名,objectfanatics@shell.sourceforge.net create
  • SSHの疎通確認
    • SSHで普通に疎通確認します。
      • Host name: shell.sourceforge.net
      • Port number: 22
      • User name: 自分の SourceForge.net のユーザー名
      • Password : なし
      • Private key file: 自分の登録した公開鍵に対応する秘密鍵のパス
コマンド単発であれば web.sourceforge.net が利用可能らしいけど、試してません。


Subversionリポジトリへの接続

バージョンは1.6です。
SVN+SSH を使う必要はなく、以下のように https でリード・ライトともに可能です。
https://objectfanatics.svn.sourceforge.net/svnroot/objectfanatics

scpその他の接続ができない場合

たとえば WinSCP の場合には以下のようなメッセージが出ることがあります。
Error skipping startup message. Your shell is probably incompatible with the application (BASH is recommended).
これは裏方では以下のようなエラーが返っている可能性があります。
You don't have an active shell at this time. For basic file transfers and
management, use web.sourceforge.net -- it allows rsync, sftp, and scp access.
If you would like to create a shell, use ssh to login using a USER,PROJECT
username with the "create" command. If you tell ssh to allocate a tty
(e.g. using -t), an interactive shell will be opened when the create is
done. Otherwise, the create command will exit when the shell becomes
ready for use. An example create that enters the shell when ready:
ssh -t USER,PROJECT@shell.sourceforge.net create
そのような場合は、interactive shell を作成後に接続しにいけばOKです。

mvn eclipse:m2eclipse

maven-eclipse-plugin は 2.8 から m2eclipse の goal が削除されたようです。そのため、明示的にバージョンを指定しないと以下のようなエラーが出ます。
mvn eclipse:m2eclipse
...
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Required goal not found: eclipse:m2eclipse in org.apache.maven.plugins:maven-eclipse-plugin:2.8
こんな場合は、バージョン 2.7 を明示的に指定しましょう。
mvn org.apache.maven.plugins:maven-eclipse-plugin:2.7:m2eclipse

2010年3月17日水曜日

GREE Developer Center への登録(その2)

Greeからメールが来ました。

国内法人であることを確認するため、以下の資料が必要とのこと。
  • 登記事項証明書
  • 絡先が記載された担当者様の名刺
これらを郵送することで、登録が完了するとのことです。

ということで、郵送しました。

2010年3月16日火曜日

資格試験受験費用の税務上の取り扱い

社員が資格試験を受ける場合に、それを会社が支払った場合に税務上どう扱われるのか調べてみた。

教育訓練費

タックスアンサーの教育訓練費の範囲に書かれている内容は結構厳しく、概要説明の段階で以下の説明がされています。
法人が使用人(役員の親族など役員と特殊の関係にある使用人及び使用人兼務役員を除きます。)の職務に必要な技術や知識を習得させるため又は向上させるために支出する費用』

法令解釈【学資金】

タックスアンサーの学資金(第14号関係)に書かれている3つの条件を満たすことにより、非課税とすることが可能だが、資格試験は無理ですね
(´・ω・`)

結論
経費として認められることはほぼありえないので、給与扱いにしておくのが正解。

2010年3月15日月曜日

改めて Google Apps Premier Edition に登録してみた

結局、別のドメイン名を取ることにしたので、以下のように登録しました。
  1. Standard Edition の画面から、ドメイン取得用のリンクを選択。
  2. comドメインを購入。年間10ドル也。
  3. 言われるがままに設定。
  4. ドメインの設定ができるまで10分くらい待つ。
  5. 全設定完了を確認。メールの疎通確認。
  6. Premier Edition へのアップグレード。
速攻でできました。 トータルで15分程度でしょうか。

Google Apps Premier Edition に登録してみたけど結局やめた件

法人として利用可能なグループウェアを探していたのだが、Google Apps Premier Edition が 30 日間無料サービスを提供していたので、評価してみることにした。

登録

Google Apps ホームページ(http://www.google.com/apps/intl/ja/business/index.html)から登録。使用するドメインも指定します。現在は一ヶ月間の無料サービスがあるが、それでもクレジットカード番号の入力が必要。


管理者アカウントの作成

登録後、google からメールが来るので、それを参考に管理者アカウントを作成する。


ドメインの所有権の確認

  1. 以下にアクセス
    https://www.google.com/a/自分のドメイン名
  2. 『ドメインの所有権を確認』をクリック。
  3. 『ドメインの所有権を確認』にて『HTML ファイルをアップロード』を選択。
  4. googlehostedservice.html という名前の HTML 確認ファイルを作成し、指定されたテキストをファイルにコピーして、 http://自分のドメイン名/ にアップロード。
  5. 確認ボタンを押下。
  6. google がドメインの所有権を確認するまで待つ。(google曰く『ドメインの所有権を Google で確認中です。完了するまでに 48 時間程度かかることがあります。』)
ということで、48時間待ちかなぁと思いきや、5分と経たずに確認完了しました。


管理者の名前に注意

ここで、管理者の名前が変更できないことに気がついた。
自分が普段使っている gmail アカウントと同じ名前でユーザーが作られてしまい、その名前を変更するためにユーザーをスイッチするにも既に規定のアカウント数のアカウントを作成してしまった。

ということで、すべて削除してから、全部やり直すことにした。

その場合、5日間は情報消しこみのため、再作成できないとのこと。

いろいろと管理者の名前変更の方法を探したけど、無理そうだ。残念。

しかし、調査する過程で Standard Edition でも当面やっていけそうなことも確認。

ということで、全部情報を削除し、5日後に Standard Edition で作り直すことにした。

請求書の送付方法

請求書の送付方法についてのメモ書き
  1. 事前に内容を確認する。
    1. 請求書のPDFをメールに添付する方法や、その他相手の指定する方法で確認を取る。
    2. 請求内容に関しての合意を得る。メールや電話等でOK。
  2. 上記の確認後、以下の方法で請求書の原本を送付する。
    1. 相手の担当者と連絡の取りやすい相手の場合は普通郵便。
    2. 上記以外は配達記録。(配達されたことの確認ができない状況を防ぐため)
  3. 請求書を送付した旨のメールを先方の担当者に送付。
  4. 請求書原本の到達確認
  5. 期限の5日前までに先方から応答のない場合はこちらから電話やメール等で確認。到達していない場合は改めて配達記録にて送付。

    2010年3月12日金曜日

    GAE/Jの実験(heapサイズ)

    GAE/J のHeapサイズの上限を実際に測ってみたくて、実験してみた。


    実験プログラム

    プログラムは、以下のようなサーブレットを作っただけ。

    private static byte[][] data = new byte[100][]; 
    private static int count = 0;
    
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
      
      // set content type
      resp.setContentType("text/plain");
      
      // show memory usage
      resp.getWriter().printf("maxMemory   = %12s\n", new DecimalFormat().format(Runtime.getRuntime().maxMemory()));
      resp.getWriter().printf("totalMemory = %12s\n", new DecimalFormat().format(Runtime.getRuntime().totalMemory()));
      resp.getWriter().printf("freeMemory  = %12s\n", new DecimalFormat().format(Runtime.getRuntime().freeMemory()));
      resp.getWriter().println();
      
      // consume 10MB
      data[++count] = new byte[1024 * 10000];
      resp.getWriter().println("count = " + count);
      resp.getWriter().println("size  = " + (count * 10) + "MB");
      resp.getWriter().println();
    
      // show memory usage
      resp.getWriter().printf("maxMemory   = %12s\n", new DecimalFormat().format(Runtime.getRuntime().maxMemory()));
      resp.getWriter().printf("totalMemory = %12s\n", new DecimalFormat().format(Runtime.getRuntime().totalMemory()));
      resp.getWriter().printf("freeMemory  = %12s\n", new DecimalFormat().format(Runtime.getRuntime().freeMemory()));
    
    }
    


    ローカルの開発環境で実行

    ローカル環境での初回アクセス時の表示。
    maxMemory   =  518,979,584
    totalMemory =   16,252,928
    freeMemory  =   11,581,272
    
    count = 1
    size  = 10MB
    
    maxMemory   =  518,979,584
    totalMemory =   26,562,560
    freeMemory  =   11,861,168
    

    16,252,928 - 11,581,272 = 4,671,656 ということで、初期のヒープ使用量は約5MB。

    ローカル環境では、以下の表示まで正常に動作。
    maxMemory   =  518,979,584
    totalMemory =  518,979,584
    freeMemory  =   31,941,032
    
    count = 48
    size  = 480MB
    
    maxMemory   =  518,979,584
    totalMemory =  518,979,584
    freeMemory  =   21,701,016
    

    次のアクセスで次のようなエラーが表示された。
    java.lang.OutOfMemoryError: Java heap space
    


    App Engine 環境で実行

    App Engine 環境での初回アクセス時の表示。
    maxMemory   =  104,857,600
    totalMemory =  104,857,600
    freeMemory  =  113,263,432
    
    count = 1
    size  = 10MB
    
    maxMemory   =  104,857,600
    totalMemory =  104,857,600
    freeMemory  =  103,023,416
    

    freeMemory が maxMemory より大きいという意味のわからない表示になった。
    freeMemory の減少は 113,263,432 - 103,023,416 = 10240016 で、ほぼ10MBの減少。


    App Engine 環境での二回目のアクセス時の表示。
    maxMemory   =  104,857,600
    totalMemory =  104,857,600
    freeMemory  =  102,853,368
    
    count = 2
    size  = 20MB
    
    maxMemory   =  104,857,600
    totalMemory =  104,857,600
    freeMemory  =   92,613,352
    

    予想通りの動作。

    App Engine 環境では、以下の表示まで正常に動作。
    maxMemory   =  104,857,600
    totalMemory =  104,857,600
    freeMemory  =   20,286,944
    
    count = 10
    size  = 100MB
    
    maxMemory   =  104,857,600
    totalMemory =  104,857,600
    freeMemory  =   10,046,928
    

    次のアクセスで次のようなエラーが表示された。
    Error: Server Error
    The server encountered an error and could not complete your request.
    
    If the problem persists, please report your problem and mention this error message and the query that caused it.
    

    ということで、100MBは使えそうです。

    100MBという数値は google で検索かけるといろんなところに出てくるので、信頼性は高いですね。

    しかし、オフィシャルな情報としてどこかに公開されているのでしょうか??


    まとめ

    Google App Engine / Java のヒープメモリについて
    • 開発環境のヒープメモリの上限は約500MB
    • App Engine 環境のヒープメモリの上限は約100MB
    • 開発環境とApp Engine 環境のヒープメモリ上限のギャップを把握しておかないと、App Engine 環境だけで OutOfMemory で泣く可能性があるので注意!

    GAE/Jのドキュメントを読んでみる その2(The Java Servlet Environment)

    以下、ドキュメントを読んだ際のメモ。

    The Java Servlet Environment

    App Engine は Java Web アプリケーションを Java 6 JVM を用いてサンドボックス内で動作する。App Engine はこの環境内で、リクエストの処理とレスポンスの準備のためにアプリのサーブレットクラスを呼び出す。


    Selecting the Java API Version

    • 現段階では、App Engine Java API は1種類しかない。
    • appengine-api-*.jar の * が API と SDK のバージョンを表す。この jar はWEB-INF/lib/ 以下に配置。
    • 互換性は保たれないバージョンがリリースされたらバージョン番号が変わる。その際、jar を差し替えない限り、既存のアプリは古いバージョンを使い続けることができる。

    Requests and Domains

    • App Engine は、どのアプリに対するリクエストなのかをドメイン名によって識別する。
    • ドメイン名が application-id.appspot.com のリクエストは、ID が application-id のアプリにルーティングされる。
    • すべてのアプリは、appspot.com のドメイン名を無料で取得できる。
    • appspot.com ドメインは subdomain.application-id.appspot.com というサブドメインをサポートしている。
    • サブドメインは、ドット(.)以外のドメイン名に含むことが許される文字列によって構成される文字列が使用できる。
    • サブドメインを使用した場合でも、そのアプリにルーティングされる。
    • 独自のトップレベルドメインを指定可能。
    • Google Apps では、独自ドメインのサブドメインに Google Mail やその他のサイトをアサインすることができ、App Engine application も同様にできる。便宜上、アプリケーションID登録時に Google Apps domainを設定しておいて、後に Administrator Console から設定することもできる。詳細はこちら
    • これらのURLへのリクエストはすべて Administrator Console でデフォルトとして設定されたバージョンのアプリケーションにルーティングされる。
    • 各バージョンのアプリはそれぞれ独自の URL を持つ。そのため、新バージョンのデプロイとテストを、新バージョンをデフォルトとする前にすることができる。
    • バージョン依存の URL は、appspot.com ドメイン名にアプリのコンンフィグファイルのバージョン識別子を使用する。パターンは version-id.latest.application-id.appspot.com
    • バージョン依存URLにサブドメインを使うことも可能。パターンは subdomain.version-id.latest.application-id.appspot.com
    • リクエストに使用されたドメイン名はアプリケーションに渡されるリクエストデータに含まれる。もしドメイン名によって異なるレスポンスを返したい場合は、リクエストデータをチェックすることで可能。

    Requests and Servlets

    • App Engine がアプリへのリクエストを受けると、デプロイメント・ディスクリプタに記述されたサーブレットを呼び出す。その際 Java Servlet API が使用される。
    • App Engine は複数のWebサーバで1つのアプリを動作させ、自動でサーバ数を調整する。そのため、与えられたリクエストはどのサーバにルーティングされるかわからず、前回のリクエストと異なるサーバにルーティングされることもある。

    Responses

    • App Engine は servlet を request と response オブジェクトを渡して呼び出し、サーブレットがレスポンスオブジェクトのデータを詰め込んで処理を返すのを待つ。servlet が処理を返したときに、response オブジェクトのデータはユーザに返される。
    • App Engine はクライアントへのデータ送信はサポートせず、より多くのアプリケーション内の処理をこなし、そしてより多くのデータを返す。言い換えれば、App Engine はリクエストを返却するためのデータストリーミングはサポートしない。
    • もしクライアントが gzip 圧縮されたデータを受け付けられる HTTP ヘッダを送ってきた場合、App Engine はレスポンスデータを自動的に圧縮し、適切なヘッダをつける。クライアントが圧縮データを確実に受け付けられるかどうかは、Accept-Encoding と User-Agent の両ヘッダを用いて識別する。独自クライアントは、Accept-Encoding と User-Agent の両ヘッダに gzip を指定することにより、圧縮を強制することが可能。
    • 自分のサイトに管理者アカウントでサインインしている状態でアクセスすると、App Engine はレスポンスヘッダにリクエスト単体の分析をつけてくれる。X-AppEngine-Estimated-CPM-US-Dollars ヘッダは、同様のリクエストが1000件来た場合の金額見積もりを表す。X-AppEngine-Resource-Usage はリクエストで使用されたリソース表し、サーバj巻、CPU時間、APIの使用したCPU時間とミリ秒単位で返す。

    The Request Timer

    • リクエストハンドラは時間制限(通常は30秒程度)を持ち、それを超えるとリクエストハンドラは処理を中断する。
    • Java実行環境は、com.google.apphosting.api.DeadlineExceededException を throw して実行を中断する。他の uncaught exceptions と同様、リクエストハンドラがこの例外を受けなかった場合、実行環境は500エラーをクライアントに返す。
    • リクエストハンドラは、レスポンスをカスタマイズするために、このエラーを catch することができる。実行環境は、独自レスポンスを返すために、この例外が起こった後に少しの時間(1秒未満)を与える。
    • リクエストの30秒ルールがあるため、App Engine は短時間で処理可能なリクエスト(数百ミリ秒程度)用に最適化されている。効果的なアプリは大部分のリクエストを即座に返す。そうしないアプリは App Engine のインフラ上ではスケールしない。

    The Sandbox

    • App Engine が複数のアプリにリクエストを分散させたり、各アプリ同士が干渉しあわないように、アプリは制限されたサンドボックス環境で実行される。この環境では、アプリはコードの実行、App Engine datastore へのデータ格納とクエリ、App Engine mail の利用、URL fetch、users services、リクエストの確認とレスポンスの準備ができる。
    • App Engine アプリができないことは以下:
    • ファイルシステムへの書き込み。アプリはデータの永続化には App Engine datastore を使用しなくてはならない。ファイルシステムからの読み出しは許可され、アプリケーションと一緒にアップロードされたアプリケーションファイルを読むことはできる。
    • socket のオープンと、他のホストへの直接的なアクセスはできない。アプリは App Engine URL fetch service を使って HTTP もしくは HTTPS リクエストを作成し、80 番ないし 443 番ポートに接続することはできる。
    • サブプロセスやスレッドを生成することができない。アプリへの Web リクエストは単一プロセス内で数秒以内で処理されなくてはならない。レスポンスを返すまでに長時間かかる処理は Web サーバの負荷超過を避けるために中断される。
    • システムコールの呼び出しはできない。

    Threads
    • java.lang.ThreadGroup や java.lang.Thread を新規に生成することはできない。これらの制約は thread を使用する JRE のクラスにも適用されている。例えば、アプリは java.util.concurrent.ThreadPoolExecutor や java.util.Timer を新規に作成することはできない。アプリは、Thread.currentThread().dumpStack() 等、現在のスレッドへの操作は可能。
    The Filesystem
    • java.io.FileWriter のようなファイルシステムへの書き込みを行うようなクラスは使用できない。java.io.FileReader のようなファイルの読み込みを行うクラスは使用可能。アプリは、Class.getResource() や ServletContext.getResource() のような形で、独自のファイルを resources としてアクセスすることも可能。
    • resource files として扱えるファイルは、ファイルシステム経由でもアクセス可能。デフォルトでは、war ファイル内のすべてのファイルは resource files として扱える。appengine-web.xml ファイルにより、resource files からファイルを除外することも可能。
    java.lang.System
    • java.lang.System クラスで App Engine に適用できないものは利用不可能になっている。
    • exit(), gc(), runFinalization(), runFinalizersOnExit() は、何も行わない。
    • inheritedChannel(), console() は null を返す。
    • アプリは JNI コードの用意や直接的な実行はできない。load(), loadLibrary(), setSecurityManager() は java.lang.SecurityException を throw する。
    Reflection
    • アプリは自分自身のクラスに対しては無制限でリフレクションを使用可能。
    • JRE と API クラスに対してもリフレクションを使用することは可能だが、public メンバのみに対してのみ可能。
    • アプリは上記以外の自分自身に所属しないクラスに対してリフレクションを使用することはできない。そして、これらの制約を回避するという理由で setAccessible() も使うことができない。
    Custom Class Loading
    • カスタムクラスローディングは App Engine でフルサポートされている。しかし、App Engine は、アプリから読み込まれたすべてのクラスに同一のパーミッションを与えるために、すべての ClassLoader をオーバーライドすることに留意する必要がある。もしカスタムクラスローディングを行う場合は、信頼できないサードパーティーのコードに注意する必要がある。

    The JRE White List

    Logging
    • アプリは java.util.logging.Logger を用いてアプリケーションログにデータを書き出すことができる。
    • アプリのログデータは Administration Console もしくは appcfg.sh request_logs によって参照したり分析したりすることができる。
    • Admin Console は Logger クラスのログレベルを認識し、異なるレベルでの表示が可能。
    • sevlet が標準出力および標準エラー出力に書き出す情報はログに書き出される。
    • 標準出力に出力された情報は INFO レベルでロギングされる。
    • 標準エラー出力に出力された情報は WARNING レベルでロギングされる。
    • log4j のようなほかのロギング用フレームワークは使用可能だが、Admin Console の log level display での詳細な制御をする場合は、ロギング用フレームワークは java.util.logging アダプタを使わなくてはならない。
    • App Engine Java SDK は logging.properties のテンプレートを appengine-java-sdk/config/user/ directory に含んでいる。利用するためには、ファイルを WEB-INF/classes ディレクトリ(もしくは war 以下の任意の場所)にコピーし、システムプロパティ java.util.logging.config.file を WEB-INF/classes/logging.properties (もしくは上記任意の場所にあわせてアプリケーションルートからの相対パスで)に指定する。システムプロパティは appengine-web.xml ファイルに書く。
    • Google Plugin for Eclipse の new project wizard はこれらのロギング設定ファイルを作成し、WEB-INF/classes/ 以下に自動的にコピーする。java.util.logging を使用するためには、このファイルを使用するようにシステムプロパティを設定する必要がある。

    The Environment

    • すべてのシステムプロパティと環境変数はアプリに閉じている。
    • システムプロパティと環境変数はデプロイメント・ディクスリプタで設定可能。
    • App Engine は実行環境を特定するために以下の2つのシステムプロパティをセットする。
    • com.google.appengine.runtime.environment は App Engine 上で実行時は Production となり、開発サーバ上の場合は Development となる。
    • com.google.appengine.runtime.version は実行環境のバージョンID。1.3.0等。
    • System.getProperty() を使用する場合、システムプロパティを type-safe API を使ってアクセスすることができる。
    • App Engine は、アプリケーションサーバ上で JVM の初期化時に、以下のシステムプロパティをセットする。
      • file.separator
      • path.separator
      • line.separator
      • java.version
      • java.vendor
      • java.vendor.url
      • java.class.version
      • java.specification.version
      • java.specification.vendor
      • java.specification.name
      • java.vm.vendor
      • java.vm.name
      • java.vm.specification.version
      • java.vm.specification.vendor
      • java.vm.specification.name
      • user.dir

    Quotas and Limits

    • 各リクエストは Requests quota にカウントされる。
    • リクエストに含まれるデータは、Incoming Bandwidth (billable) quota にカウントされる。
    • レスポンスに含まれるデータは、Outgoing Bandwidth (billable) quota にカウントされる。
    • HTTP と HTTPS リクエストは Incoming Bandwidth (billable) quota と Outgoing Bandwidth (billable) quota にカウントされる。
    • Admin Console の Quota Details ページでは、Secure Requests、Secure Incoming Bandwidth、Secure Outgoing Bandwidth を別々にレポートし、HTTPS リクエストのみがカウントされる。
    • CPU 処理時間は CPU Time (billable) quota にカウントされる。
    • quotas に関する更なる情報は、Quotas や Admin Console の "Quota Details" セクションを参照のこと。
    • 更に quotas には、リクエストハンドラに以下の制限が適用されている。
    Limit Amount
    request size 10 megabytes
    response size 10 megabytes
    request duration 30 seconds
    simultaneous dynamic requests 30 *
    maximum total number of files (app files and static files) 3,000
    maximum size of an application file 10 megabytes
    maximum size of a static file 10 megabytes
    maximum total size of all application and static files 150 megabytes
    * アプリは30リクエストの同時接続が可能。これは、他の追加レイテンシを無視すると、サーバーサイドの平均処理時間を 75ms の場合に (1000 ms/second / 75 ms/request) * 30 = 400 requests/second の処理が可能なことを意味する。CPU負荷の高いアプリの場合は、同一サーバで並行するほかのアプリのために長時間実行されるリクエストの追加レイテンシを被る。静的ファイルへのアクセスは、この制約に該当しない。
    • もしアプリがリソースを効果的に利用し、トラフィックが秒間最大数を超えてしまいそうな場合は、同時アクセス数の制限を上げる要求をすることができる。App Engine は30同時アクセス以上にスケールすることができる。このデフォルトの制限は、パフォーマンスの悪いアプリや、リソースを溜め込む悪意のあるアプリへの対策として存在する。


    おいら的重要事項
    • サブドメインが使える。
    • 独自のトップレベルドメインを指定可能。
    • アプリのバージョン毎に異なるURLを持つことができる。
    • ドメイン名はアプリケーションに渡されるリクエストデータに含まれる。
    • ロードバランス時にサーバ・アフィニティは使えない。
    • Accept-Encoding と User-Agent の両ヘッダに gzip が指定されていると、App Engine が自動的によきに計らってくれる。
    • 自分のサイトに管理者アカウントでサインインしている状態でアクセスすると、App Engine はレスポンスヘッダにリクエスト単体の分析をつけてくれる。
    • リクエストの30秒ルール。
    • 30秒を超えた場合の例外は catch することができ、1秒未満だが独自レスポンスを返す時間が与えられる。
    • App Engine のインフラは、リクエストを数百ミリ秒程度で返す場合を想定して最適化されているため、そうしないとスケールしない。
    • ファイルシステムへの書き込みはできない。
    • スレッドの生成はできない。
    • socket を直接使うことはできず、URL fetch service を使って HTTP もしくは HTTPS リクエストを作成し、80 番ないし 443 番ポートに接続することはできる。
    • リフレクションは、自分自身のクラスに対してはフルサポート、JREとAPIのクラスに対しては public メンバに対してのみ可能、その他のクラスに対しては不可能。
    • sevlet が標準出力および標準エラー出力に書き出す情報はログに書き出される。
    • 同時接続数のデフォルトは 30 だが、静的ファイルへのアクセスはこの制約に含まれない。
    • ファイルサイズの上限は静的ファイルもそれ以外も10MBまで。
    • 全ファイルの合計は150MBまで。

    疑問点
    ・1インスタンスで利用可能なメモリサイズはどの程度か?特にヒープサイズは、ホスト内にキャッシュを持つ場合に重要。→ GAE/Jの実験(heapサイズ)参照。100MBまではOKっぽい。

    2010年3月11日木曜日

    GAE/Jのドキュメントを読んでみる その1(App Engine Java Overview)

    以下、ドキュメントを読んだ際のメモ。

    App Engine Java Overview

    • GAEを用いることにより、標準的なjava技術を用いてWebアプリケーションを開発することができ、それらをGoogleのスケーラブルなインフラ上で動作させることができる。
    • Java 環境は Java6 JVM や Java Servlets interface を提供する。また、JDO, JPA, JavaMail, JCache 等の標準的なインタフェースを、App Engine のスケーラブルなデータストアやサービスをサポートするために提供している。標準仕様のサポートはアプリケーション開発を簡単にし、また独自の servlet 環境へのポーティングを容易にする。Google Plugin for Eclipse は、新しいプロジェクト・ウィザードやデバッグ設定を提供する。
    • GAE/J は、Google Web Toolkit (GWT)により、ワールドクラスのWebアプリケーションの開発とデプロイを容易にする。
    • Eclipse plugin は、App Engine と GWT の SDK を同梱している。
    • 他のJava用IDEと同様、サードパーティのプラグインも存在する。NetBeans や IntelliJ など。
    • まだ入手していないなら、the Java Getting Started Guide を読みましょう。

    The Java Runtime Environment

    • JVMは java6。
    • App Engine SDK は Java5 以降をサポート。(Java 6 JVMは Java6 までの他のバージョンのコンパイラでコンパイルされたクラスを扱うことができる)
    • App Engine は Web アプリケーションに Java Servlet 標準を採用している。そのため、servlet クラス、JSPs、静的ファイル、データファイルを、デプロイメント・ディスクリプタ (web.xmlファイル) や他の設定ファイルを、標準仕様どおりの WAR ディレクトリ構成で用意することができる。
    • App Engine は リクエストをデプロイメント・ディスクリプタにしたがって呼び出されたサーブレットによって処理する。
    • JVM は安全なサンドボックス環境で実行される。
    • サンドボックスは、アプリケーションが他のアプリケーションのパフォーマンスやスケーラビリティに干渉しないことを保証する。例えば、アプリケーションは、スレッドの生成、ローカルファイルへの書き込み、自由なネットワークコネクションの確立等ができない。また、JNI他のネイティブコードを扱えない。JVM は、サンドボックスの中では、Javaのどのようなバイトコードでも実行することができる。
    • 更なる情報は、Servlet Environmentを参考のこと。

    The Datastore, the Services and the Standard Interfaces

    • App Engine は、スケーラブルなサービス群を提供する。それはアプリケーションから、データの永続化、ネットワーク上のリソースへのアクセス、イメージデータの操作のようなほかのタスク等のために利用することができる。
    • これらのインタフェースはポーティングを可能とするために、可能な限り標準APIに準拠している。
    • 各サービスは、新たなインタフェースアダプタの開発や直接的なアクセスのため、完全な低レベルを提供している。
    • アプリは、App Engine Datastore を信頼性のあるスケーラブルなデータストレージとして使用することができる。このデータストアは2つのJavaの標準的なインタフェースである Java Data Objects (JDO) 2.3 と Java Persistence API (JPA) 1.0 に対応している。これらのインタフェースは、オープンソースの実装である DataNucleus Access Platform を利用して実装されている。
    • App Engine Memcache は、データストアへのクエリや演算結果のキャッシュ用の高速かつ一時的な分散ストレージを提供する。Java のインタフェースは JCache (JSR 107)。
    • アプリは Web 上のリソースにアクセスや、HTTP や HTTPS プロトコルを用いて他のホストと通信するために、URL Fetch service を利用することができる。Java からは普通に java.net.URLConnection とその関連クラスを使えばよい。
    • アプリは、アプリケーションの管理者やログイン中のユーザに代わってメールを送信するために、Mail サービスを利用することができる。Java からは普通に JavaMail を使用すればよい。
    •  Images サービスは、さまざまなフォーマットのイメージデータについて、トリミング、回転、リサイズ、photo color enhancement などを含む、変換や操作の機能を提供する。このサービスは CPU 負荷の高い画像処理タスクを受け持ち、アプリケーションサーバにWebリクエストを処理させるリソースを残す。(もちろん、アプリケーションサーバ上でサンドボックス制約の範囲内でJVMベースの画像処理ソフトを使うこともできる)
    • アプリは、Google Accounts をユーザー認証に利用することができる。Google Accounts は、ユーザーアカウントの作成、ログインができ、既にアカウントを持っているユーザはそのアカウントをアプリ上で使うことができる。アプリは現在ログイン中のユーザを検出することができ、そのユーザのemailアクセスを使用することができる。アプリは、Google Accounts 経由でのアクセスに対して、デプロイメント・ディスクリプタで制約をかけることができ、ユーザーがサインインしているかどうかを検出することができ、servlet request の getUserPrincipal() メソッドで email アドレスを取得できる。アプリは低レベルのGoogle Accounts API で、サインインおよびサインアウトの URL の作成や、datastore 内の storage に適したユーザーデータオブジェクトを取得することができる。(メモ:guestbookサンプルアプリでいきなり com.google.appengine.api.users.User を永続化できた理由はコレか。)

    Scheduled Tasks

    • アプリは、定期的にアプリの URL を呼び出すスケジュールされたタスクを設定することができる。詳細は Cron Jobs を参考のこと。

    Java Tools

    • App Engine Java SDK は、アプリのテスト、アプリケーションファイルのアップロード、ログデータのダウンロードツールを含んでいる。また、App Engine は Apache Ant がプロジェクトに共通のタスクを簡略化するためのコンポーネントを含んでいる。Google Plugin for Eclipse は Eclipse に App Engine 開発、テスト、デプロイ機能を追加する。また、完全な App Engine SDK を含む。また、Eclipse plugin は Google Web Toolkit アプリケーションの開発と App Engine 上での実行を容易にする。
    • 開発サーバ は、開発とテストのためにローカルコンピュータ上で動作する。サーバはデータストア、サービス群、サンドボックスの制約をシミュレートする。また、開発サーバは、アプリがテスト中に実行したクエリを元にデータストア・インデックスの設定を生成することができる。
    • AppCfg という多目的ツールは、App Engine 上で動作する自分のアプリとのすべてのコマンドライン操作を扱う。AppCfg はアプリの App Engine へのアップロードや、コードをアップデートする前に新しいインデックスを構築するためにデータストアインデックス設定のみを更新することができる。また、アプリのログデータをダウンロードすることもできるので、自前のツールでアプリのパフォーマンス分析をすることもできる。
    GAE/Jのドキュメントを読んでみる その2(The Java Servlet Environment)に続く。

    Google App Engine(GAE)の調査 その2

    実行できるWebアプリケーション数

    https://appengine.google.com/ から、10個まで登録可能。


    Using JSPsを読んでみる(link)

    基本的な jsp と servlet の使い方で、特筆すべきことは特にないが、GAE 依存情報として、war/WEB-INF/appengine-web.xml にシステムプロパティを設定する例が示されている。(java.util.logging がサンプル)

    しかし、このドキュメントの説明だけでは、本番環境でどのようにログを見ればよいのかわかりまてん。

    ドキュメントの Logging の章を参考にして、以下を実行して正常動作することを確認しました。
    • Administration Console から確認
      •  実際にサーバ上で試したところ、AdminConsole の Main の Logs の項目に表示されました。
    • appcfg.sh request_logs を使用してダウンロード
      • eclipse\plugins\com.google.appengine.eclipse.sdkbundle.1.3.1_1.3.1.v201002101412\appengine-java-sdk-1.3.1\bin にパスを通す。
      • java にパスを通す。
      • ワークスペース直下をカレントディレクトリに指定。
      • appcfg.cmd --severity=0 request_logs Guestbook\war guestbook_log.txt を実行。
    appcfg.sh request_logs は、--severity 指定なしだとアクセスログで、指定するとそのレベルのログが出るようです。また、デフォルトでは UTC時刻で当日分だけになるので、必要があれば -n NUM_DAYS にて日数を指定します。


    Using the Datastore with JDOを読んでみる(リンク)

    言われたとおりにやればOK。JDO での読み書きと簡単な JDOQL を一通り試せました。

    しかし、こんな感じでデータ作りまくると、ゴミ掃除とかどうすんだろ?プログラム組むのかな?普通にRDB使うときのように、複数のDB(Oracleならスキーマ)を用いたり、アドホックにデータを参照したりしたくなるよね。なんかいいフロントエンドあるのかな?

    それらしいドキュメントは一見なさそうに見える。ググっても同様。
    まぁ、とりあえず先に進もう。


    Using Static Files を読んでみる(リンク

    GAEでは、デフォルトで war 配下のファイルは、 JSP と WEB-INF 配下のファイル以外は静的ファイルとして扱われるようになっていますが、効率のため servlet を呼び出すサーバとは別サーバに静的ファイルを置く方法も提供しているとのことです。

    実際に hoge.txt を war 直下に置いてデプロイしてみましたが、問題なくアクセスできました。

    ちなみに、パスが servlet や filter マッピングにマッチしても、静的ファイルは直接ブラウザに返されるとのこと。

    どのファイルを静的ファイルとして扱うかは、appengine-web.xmlによって設定できるとのこと。(Java Application Configurationにて説明されている)

    ということで、CSS を適用するサンプルがあるので試してみる。ちなみに、このサンプルでは、appengine-web.xml は変更しない。

    実際に実行してみた。問題なし。


    Uploading Your Application を読んでみる(リンク

    既に試したことがあるので読み飛ばそうとも思ったが、一通り読むだけ読んでみた。

    読んどいてよかったw
    以下のような記述があった。
    Note: Once you register an application ID, you can delete it, but you can't re-register that same application ID after it has been deleted. You can skip these next steps if you don't want to register an ID at this time.
    application ID を削除すると二度と同じIDを使えないということなので、application ID の作成と削除はかなり慎重に行ったほうがよさそうです。

    ということで、Getting Started: Java 完了!

    会計ソフトの導入(弥生会計のデモ版)

    そろそろ会計ソフトを導入しようか?

    今まで経理情報はスプレッドシート上で管理していたのだが、創立後初めての月の終了し、これから月の締め作業、経費清算、給与支払い等をしていくにあたって、さすがに会計ソフトを導入しないとやっていけないなぁと思う今日この頃。

    ということで、そろそろ会計ソフトの導入を検討しようと思い立った。


    会計ソフト選び

    さて、まずはどのソフトを導入するかを決めなくてはならない。昔フリーランスで仕事をしていたときは個人事業主として青色で申告していたのだが、そのときは弥生の青色申告を使っていた。特に不満はなかったので、今回もまずは弥生を使ってみることにした。

    まずは、弥生の青色申告と会計の違いを確認。弥生会計は法人決算や給与明細を作れるとのこと。ということで、今回は弥生会計をチョイス。

    値段は、ヨドバシで34,800円。安くはないなぁ、、、。まぁ、いいか。


    弥生会計のデモ版を試してみる

    まずは、弥生会計のページに行き、株式会社の確定申告に必要な情報を一括管理できるか確認することにした。個人事業主の時は数枚の資料を印刷しただけであっという間に出来上がりという楽チンさでしたが、株式会社の場合はどうなんだろう?→ 弥生会計で確定申告に詳しく書かれていそうなので、あとでチェックしてみることにした。

    ということで、オンラインデモを見てみることにした。

    オンラインデモはインタラクティブなチュートリアル形式で結構わかりやすい。

    あとで実際にデモ版を落として本番のデータを入れる時にチュートリアル思い出しながらやるのもなんなので、デモ版を落として同時並行でデータ入力することにしようと思い立った。しかし、デモ版に入力したデータを製品版に移行できなきゃ意味ないよね。調べてみると、以下のような説明を発見。問題なさそう。
    弥生会計 10 体験版では、サンプルデータをお使いいただくだけではなく、お客様のデータを新規作成することが可能です。
    また、新規に作成したデータは、製品版でもお使いいただけます。

    ということで、デモ版をダウンロード。

    実際に今までのデータを入れてみた。特に問題なさそう。


    参考サイト

    この弥生会計で確定申告というサイトは、かなり詳しく説明されているので、後でチェックしてみようと思う。


    現金主義か発生主義か

    ちなみに、最初のうちは経費は年度を跨ぐ時以外は現金主義で扱うつもりだったのだが(この方法は小企業では事実上黙認されているような話をどこかで聞いた)、経費が発生したタイミングで弥生会計にインプットしていく場合には発生主義の方が二度手間にならないので、発生主義で扱うことにした。


    締め日と支払日

    弥生とは関係ないけど、締め日ネタ。

    今は経理の手間を削減するために、経費や給与について月末締め翌月払いにしている。そのため、完全に締めて、その後余裕を持って計算することができる。

    年度も1月から12月にしているので、企業会計的な締めと税務的な締めが一致しているので、二度手間にならない。(と、今のところ思っている。)

    経理専門の社員がいない会社だったら、こんな感じでいいんじゃないかなぁと思う(けど、実際どうなのかは後で身を持って知ることになるであろう)。


    とりあえずの結論

    ひとまず、2月度の締めと支払いをすべてデモ版で行ってみて、特に問題なければ製品版を購入することにしよう。

    2010年3月8日月曜日

    Google App Engine(GAE)の調査 その1

    今回は、Google App Engine(GAE)を調査してみようとおもう。

    調査の動機

    長期的にドメインモデルを保守し変化し続けることができる能力がが自分達のコアコンピタンスの1つだと確信しているので、NoSQL にはそれほど興味がない。そして現在のGAEが採用する永続化技術は NoSQL の一種である BigTable とファイルシステムである GFS だ。しかし、以下の理由から GAE を調査してみることにした。
    • ドメインモデルを永続化する用途以外では NoSQL も十分価値があると思えるし、その適用場所を見極めるためにも経験が必要。
    • Google がこれだけ力を注ぎ込んで開発したものだから、GAE 自体がベストプラクティスの固まりだと考えるが妥当。そのため、GAE を使い込んで理解することは今後の自分たちに技術向上に直結する。
    • 将来的に自分達のサービスを提供する場合に、GAE だけで構成することはおそらくないだろうが、部分的にGAE を使うことは十分考えられるので、大いに研究しておきたい。
    • 自動的にスケールするアーキテクチャは魅力。
    • 500MBのストレージと500万PV/月まで無料というのは、スタートアップである自分達にとってはかなり魅力的。(EC2も検討していたが、利益の目処も立たないうちからEC2を使うというのは経済的に好ましくない。)

    まずは本家のサイトからチェック

    http://code.google.com/intl/en/appengine/
    • まずは blog post をチェック。
      • 環境とかツールに関しての情報が書かれているので一読。
      • Google Plugin for Eclipse はほぼ必須なのかな?
      • Cronとか、DBインポートとか、もうすぐDBのエクスポートができるようになる予定とか。
    • Campfire One announcements を見てみる。
      • 特徴とか、Eclipse環境でGWT使ったデモとか、パートナー企業の事例デモとか。
      • 見なくてもいいけど、そこそこ面白いので見ておいて損はない。
    ある程度概要はわかってきたので、そろそろ手を動かすことにする。


    Getting Started Guide

    Getting Started Guideを読んでみる。


    Installing the Java SDK

    普通にJava6を入れてみた。


    Getting Eclipse

    Getting Eclipse

    FlushBuiderのplug-inを入れる予定なので、Eclipse3.4(Ganymede)を入れることに。
    eclipse-jee-ganymede-SR2-win32.zip をダウンロード。


    Installing the Google Plugin for Eclipse

    Installing the Google Plugin for Eclipse

    Eclipseを展開し、起動し、Google Plugin を Software Update でインストール。
    work with に http://dl.google.com/eclipse/plugin/3.4 を指定。
    すべてのオプションをインストール。


    Creating a Project

    Creating a Project

    とりあえず、GWTなしバージョンの説明どおりにやってみた。
    logging.properties の場所が、上記リンク先では src 内だが、実際は WEB-INF 以下だった。

    とりあえず、プロジェクト作った直後の状態では、text/plain で Hello, world 文字列を返す Servlet が作られるので、デバッガで起動してみた。

    問題なく起動できました。
    こりゃお手軽です。

    GuestbookServlet.java を変更して保存してブラウザをリロードしたところ、変更が反映されてました。
    こりゃお手軽です。


    Uploading to Google App Engine

    Uploading to Google App Engine

    とりあえず、Application Identifierを適当に設定。
    独自ドメインも後で簡単に設定できるっぽい。

    appengine-web.xml に作成したIDを書き込んで保存。

    デプロイボタンを押してみた。
    あっさりデプロイされてました。
    こりゃお手軽です。


    Using the Users Service

    Using the Users Service

    言われるがままに GuestbookServlet.java を変更し、ローカルで実行。
    エミュレーションされてるっぽい。

    デプロイしてサーバにアクセス。
    ちゃんとGoogleアカウントのログインページ経由になりました。

    以下 doGetメソッドのソース
    ---
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
      // ファクトリから UserService を取得
      UserService userService = UserServiceFactory.getUserService();
      
      // 現在の google アカウントのユーザを取得
      User user = userService.getCurrentUser();
      
      // 既に google アカウントでログインしていた場合
      if (user != null) {
        resp.setContentType("text/plain");
        resp.getWriter().println("Hello, " + user.getNickname());
      }
      
      // まだ google アカウントでログインしていない場合は、google 側で用意されるログインページにリダイレクト
      else {
        resp.sendRedirect(userService.createLoginURL(req.getRequestURI()));
      }
    }
    
    ---

    ノートPCのバッテリーが切れそうなので、今日はここまで。

    2010年3月6日土曜日

    BSON

    BSON
    BSON is a bin­ary-en­coded seri­al­iz­a­tion of JSON-like doc­u­ments.

    JSONのようなデータのバイナリ版。
    C, C++, Java, PHP, Ruby, Python 等に対応している。

    仕様は http://bsonspec.org/ に公開されている。

    2010年3月4日木曜日

    Stripgenerator

    Stripgenerator

    4コマ漫画とか風刺漫画的なものを簡単に作成できるサイト。

    人とかモノとか図形とかがそれなりにそろえられていて、それらをドラッグ・アンド・ドロップして配置し、大きさを変えたりできる。そして吹き出しを配置してその中に台詞を入れれば、できあがり。

    あ、日本語化けた。

    How come that Groovy++ overperform Java?

     How come that Groovy++ overperform Java?

    おいらは Groovy が出た頃に、静的な性格を持ちつつシンタクティック・シュガーのみを享受できればいいなぁと思っていたのですが、上記のページにある Groovy++ ってまさにそれなのか?
    statically typed Groovy is an atempt to combine performace and compile-time checks with the expressiveness of the Groovy programming language and to develop high-performant runtime libraries utilizing this combination.

    サポートのこととか考えるとプロダクションではまだ使えない気がするけど、動向をウォッチするのはいいかもしれない。

    2010年3月3日水曜日

    GREE Developer Center への登録

    とりあえず、GREE Developer Center に登録してみることにした。

    ホームページから登録ページに移動すると、登録フォームが表示されます。

    フォームには、以下のような説明がありました。
    • デベロッパーセンター登録は、法人様のみ対象。
    • 法人格等の確認後IDやパスワードを提供。
    • IDやパスワードは、ログイン開始と時期を合わせて提供。
    • 開発環境のご利用やアプリケーションの公開には、別途契約が必要。
    ということで、会社や担当者の情報を入力して送信。
    法人格の確認などをさせて頂いた上で、IDやパスワードをご提供いたします。IDやパスワードは、ログイン開始と時期を合わせてご提供いたしますので、発行までお時間を頂く可能性がございます。
    とのことです。

    GREE Developer Center について調べてみた。

    ホームページはこちら。
    http://developer.gree.co.jp/

    GREE Platform とは何ぞや?
    • GREEをプラットフォームとして、アプリケーションサービスを提供していただける仕組。
    • ソーシャルグラフや更新情報の取得、課金システム等の機能をAPIとして提供。
    • OpenSocialに準拠。

    GREE Connectとは何ぞや?
    • GREEのソーシャルグラフ情報や更新情報を、外部のサービスや、スマートフォン等の各種デバイスから利用できる仕組み。
    • 外部サービス、デバイスをGREE Connectに対応していだくことにより、GREEと連動した新しいサービスの開発や、既存サービスの拡張が可能となる。
    • ユーザはGREE本体サイトに限らず、さまざまなサービスやデバイスから、情報を更新することが可能になる。
    • OpenSocialのRESTful Data APIによるGREEのソーシャルグラフをはじめとしたさまざまなデータを外部から操作するWeb APIを提供する。
    • GREE Connectの各APIはOAuthによってユーザー認証とデータアクセスの認可を行う。

    おいらの理解

    ん~、よくわかりません (´・ω・`)

    とりあえず法人登録してみることにします。

    Tech Brief: NoSQL for DBAs

    Tech Brief: NoSQL for DBAs

    MySQL(今はSunですが)の Brian Aker の NoSQL についてのライトニングトーク。

    かなり笑える。

    Groovy/Grails/GAE

    Groovy再考

    Groovy には昔結構入れ込んだ。しかし、仕事で使うことはなかった。なぜかと言うと、動的言語を用いるとプロジェクトのアプリケーションの品質を保てないことが明白だったからだ。高品質なアプリケーションを開発するためには Groovy のほうが Java よりもはるかに開発者に高い能力を要求する。

    しかし、自分で会社を始めた今、自分の権限でメンバーを選ぶことができる。現状は自分を含めて2名のエンジニアしかいないわけだが、動的言語でも品質の高いアプリケーションを書くことができると確信している。

    そうなると、java よりは Groovy のほうが適切なのではと考えるようになった。

    Grails再考

    また、Grails については、 Grailsそのものの品質がまだ見えていなかったことや、Grails のコミュニティが他の動的言語のコミュニティのように見かけだけの生産性のみを追及するのではないかという懸念があったため、手を出さないでいた。

    しかし、SpringSource が Groovy を獲得したため、見かけだけの生産性を追及するような懸念はほぼ払拭できている。

    さらに、 VMWare が SpringSource を買収したことにより、クラウド、アプリケーションサーバ、Web系フレームワーク、DIコンテナ、言語、これらのすべてがワンストップで提供される可能性が出てきた。

    そうなると、この流れに乗っておきたい気がする今日この頃である。

    GAE再考

    クラウドについては GAE についても検討しているのだが、正直まよっている。確かにメリットは多いのだが、許容できるレベルにないデメリットが数多く存在するのが問題だ。

    大量のデータのインポートやエクスポートができないとか、join が使えないとか、リクエストの処理時間に制約があるとか、これらは我々の開発方針としては許容できない。しかし、これは時間が解決してくれる可能性もある。

    BigTable については、現状においては DB のテーブル設計が神という考え方をする人たちには向いていると思うのだが、オブジェクト指向設計を行う人たちには RDB よりも敷居が高いと考えている。理由は、オブジェクトとのインピーダンスミスマッチが RDB よりも BigTable のほうが大きいからだ。そして、その影響は、洗練されたオブジェクト指向設計であればあるほど大きくなる。トランザクションその他の機能についても、BigTable には不利な点が多い。しかし、メリットも当然多いはずなので、メリットとデメリットを十分に見極める必要がある。

    最初からデータ中心の設計を行い、ドメインモデルではなくトランザクションスクリプトを用いる可能性も視野に入れつつ GAE の使い方を検討してみるのもいいかもしれない。

    なんてこと考えてました。

    MetaphoricQuestioning

    MetaphoricQuestioning

    Comparing to another activity is useful if it helps you formulate questions, it's dangerous when you use it to justify answers.

    この考え方はいい。

    しかし残念ながら、前者が使われることは少なく、後者の使われ方がほとんどのように感じます。

    個人的な経験でも、メタファの導入を支持してもらえるプロジェクトは過去に2つだけでした。

    特に今日の多くのプロジェクトは、「概念とか設計とかどうでもいいから手を動かせ」という風潮が強いですからねぇ。