2009年12月31日木曜日

2009年の記事数(442) > 2008年の記事数(441)

スズキです。

今年最後は、近頃始めた名言シリーズでしめようと思います。
http://blog.suz-lab.com/search/label/Maxim

> 他人と比較して、他人が自分より優れていたとしても、それは恥ではない。
> しかし、去年の自分より今年の自分が優れていないのは立派な恥だ。

イギリスの探検家、ラポックの言葉です。

ということで、本記事で、2009年の記事数442となり、
2008年の441をぎりぎりですが、抜くことができました!

2010年の「suz-lab」はブログ以外でも、いろいろと展開していこうと思っています。

乞うご期待!

--------
http://www.suz-lab.com

OvalFormResolverをpublicフィールドに対応(T2)

スズキです。

こちらで、T2のpublicフィールドに関してつぶやいていたら、
http://blog.suz-lab.com/2009/12/ovalpaget2.html
shot6さんからTwitterで下記コメントをいただきました。
> 今のところpublicフィールドはサポートしてないんです。
> 問題が多いので多分この先もサポートしないと思います。ごめんなさーい。

と言うことで、じゃあ、OvalFormResolverのときだけでも、と思い、
下記のように実装してみました。

--------【Java】--------
...
@Override
public void resolve(Form form, WebContext context, Object object,
ErrorInfo info) {
  super.resolve(form, context, object, info);
  this.setPublicFields(context.getRequest(), object, info);
  this.validateForm(object, info);
}

private void setPublicFields(Request request, Object object, ErrorInfo info) {
  for(Field field : object.getClass().getFields()) {
    Object value = request.getParameter(field.getName());
    if(value != null) {
      try {
        field.set(object, value);
      } catch(IllegalAccessException e) {
        info.addErrorInfo(e.getMessage(), e);
      }
    }
  }
}

private void validateForm(Object object, ErrorInfo info) {
  Validator validator = new Validator();
  List<ConstraintViolation> violations = validator.validate(object);
  for(ConstraintViolation violation : violations) {
    info.addErrorInfo(violation.getMessage(), new
ConstraintsViolatedException(violation));
  }
}
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/t2/OvalFormResolver.java?r=89

見ての通り、かなり適当です…(全然テストもしていません…)
必要に応じて、精度を上げてくって感じですね…

ようやく、マジック1だ!今年最後のネタは何にしようかなー?

--------
http://www.suz-lab.com

非同期通信(Ajax)用のJSONフォーマット(T2)

スズキです。

いろいろ考えましたが、結局こんな形になるようにしました。

--------【成功】--------
{
  "errors": [],
  "params": {
    "url": "http:\/\/www.suz-lab.com"
  },
  "results": {
    "url":"http:\/\/www.suz-lab.com"
  }
}
--------

--------【失敗】--------
{
  "errors": [
    "suz.lab.feed.page.other.mypage.IndexJson$InsertForm.urlは最小値(3.0)を下回っています。"
  ],
  "params": {
    "url": "http:\/\/www.suz-lab.com"
  },
  "results": null
}
--------

正常終了時には、"results"に任意の結果が、
異常終了時には、"errors"にエラーメッセージの配列が、
返ってくるようにしています。

また送信パラメータは"params"で、そのまま返ってくるようにしています。

Pageクラスでこんな感じに書けるようにしました。
(実装が、まだ、甘過ぎですが…)

--------【Java】--------
...
@Ajax
@POST
@ActionPath
public Navigation insert(
  @Form(resolverClass=OvalFormResolver.class) IndexJson.InsertForm form,
  ErrorInfo info
) {
  return (new JsonResponse<IndexJson.InsertForm, Map<String, String>>() {
    @Override
    protected Map<String, String> createResults() {
      Map<String, String> map = new HashMap<String, String>();
      map.put("url", this.getParams().getUrl());
      return map;
    }
  }).createResponse(form, info);
}
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/src/suz/lab/feed/page/other/mypage/IndexJson.java?r=79
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/t2/JsonResponse.java?r=78

JsonResponseクラスの中の正常終了時の結果を作成する抽象メソッド
(createResults)をオーバーライドすることで利用出来るようにしています。
(createResponseを実行することで、実際にJSONオブジェクトを作成しています)

JSONレスポンスを処理するJavascript(jQuery)は、こんな感じです。

--------【jQuery】--------
$(function() {
  $("#form").validate({
    submitHandler: function(form) {
      $(form).ajaxSubmit({
        dataType: "json",
        success: function(json) {
          if(json.errors.length == 0) {
            alert(json.results.url);
          } else {
            for(var i = 0; i < json.errors.length; i++) {
              alert(json.errors[i]);
            }
          }
        }
      });
    }
  });
});
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/war/other/js/app/mypage/index.js?r=79

マジック2になった、あと5時間...

--------
http://www.suz-lab.com

SimpleContainerAdapterでコンポーネントを初期登録(T2)

スズキです。

"resolverClass=OvalFormResolver.class"を使いたいがために"Guice"を導入したのですが、
http://blog.suz-lab.com/2009/12/ovalpaget2.html
shot6さんからTwitter経由でこんなアドバイスをいただきました。
(大晦日にも関わらず、ありがとうございます!)
> できます。web.xmlにt2.componentsという指定でクラスを登録することが出来ます。ただしシングルトンオンリーかつ難しい事はできないです。
> 設定例はこれです>http://code.google.com/p/t-2/source/browse/trunk/samples/t2-samples/src/main/webapp/WEB-INF/web.xml

と言うことで、web.xmlをGuice関係の記述をはずし、下記のように修正しました。

--------【web.xml】--------
...
<filter-name>t2</filter-name>
<filter-class>org.t2framework.t2.filter.T2Filter</filter-class>
<init-param>
  <param-name>t2.rootpackage</param-name>
  <param-value>suz.lab.feed.page</param-value>
</init-param>
<init-param>
  <param-name>t2.exclude-resources</param-name>
  <param-value>txt, css, js, ico, png, gif, jpg</param-value>
</init-param>
<init-param>
  <param-name>t2.components</param-name>
  <param-value>suz.lab.gae.t2.OvalFormResolver</param-value>
</init-param>
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/war/WEB-INF/web.xml?r=85

さよなら、Guice ...

--------
http://www.suz-lab.com

2009年12月30日水曜日

OValを使ったPageクラス(T2)でのバリデーション

スズキです。

T2の非同期通信(Ajax)までは形になったので、
http://blog.suz-lab.com/2009/12/query-t2ajax.html
次は、リクエストパラメータのバリデーションをどうするか?です。

今回は、バリデーションフレームワークとして、
OValを利用することにしました。
http://oval.sourceforge.net/

具体的には、""/mypage/index.json/insert"で処理されるメソッドを、
下記のようにしています。

--------【Java】--------
...
@Ajax
@POST
@ActionPath
public Navigation insert(
  @Form(resolverClass=OvalFormResolver.class) IndexJson.InsertForm form,
  ErrorInfo info
) {
  return (new JsonResponse<IndexJson.InsertForm, Map<String, String>>() {
    @Override
    protected Map<String, String> createResults() {
      Map<String, String> map = new HashMap<String, String>();
      map.put("url", this.getParams().getUrl());
      return map;
    }
  }).createResponse(form, info);
}

public static class InsertForm {
  private String url;
  public String getUrl() { return url; }
  public void setUrl(String url) { this.url = url; }
}
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/src/suz/lab/feed/page/other/mypage/IndexJson.java?r=77

ポイントは、
"@Form"、"resolverClass=OvalFormResolver.class"、"IndexJson.InsertForm"、"ErrorInfo"
の引数まわりです。

【@Form】
T2は"@Form"がついたPOJOに対して、リクエストパラメータをマッピングしてくれます。
OValはPOJOにアノテーションつけて、バリデーション処理するので、
ちょうどいいかなー、って感じです。

【resolverClass=OvalFormResolver.class】
で、マッピングするときに、POJOにつけたOValアノテーションで
バリデーションまでやっておきたいなーと思い、
実際にマッピングを行うクラス"OvalFormResolver"を作成し、
それを利用するように指定してます。(この辺は後で詳しく書きます)

【IndexJson.InsertForm】
リクエストパラメータがマッピングされるPOJOです。
OValのアノテーションを必要に応じて入れます。
いろいろ試しましたが、パブリックな静的インナークラスに落ち着きました。

【ErrorInfo】
マッピング時にエラーが発生した時のエラー情報が入っています。
"OvalFormResolver"で、Ovalバリデーション時のエラー情報も入るようにしています。

実際の"OvalFormResolver"は以下のようになっています。

--------【Java】--------
...
public class OvalFormResolver extends FormResolverImpl {
  @Override
  public void resolve(Form form, WebContext context, Object object,
ErrorInfo info) {
    super.resolve(form, context, object, info);
    Validator validator = new Validator();
    List<ConstraintViolation> violations = validator.validate(object);
    for(ConstraintViolation violation : violations) {
      info.addErrorInfo(violation.getMessage(), new
ConstraintsViolatedException(violation));
    }
  }
}
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/t2/OvalFormResolver.java?r=71

上述したように、OValバリデーション処理して、エラーをErrorInfoに追加して
って処理になってます。

ここで、大問題が発生です。実際に実行すると、
resolverClass=OvalFormResolver.class
がききません...

ということで、下記の記事でも紹介している"Guice"の登場です。
http://blog.suz-lab.com/2009/12/mobylet-guice-t2-velocity-on-gaej.html

Guiceを利用することで、無事、"OvalFormResolver"が利用できるようになりました。
(実は長い道のりでした...)

ただ、一点、まだ何とかしたいことがあって、それは、
リクエストパラメータをマッピングするPOJOにgetter/setterを書かなければならないことです。

できれば、Seasar2のPublicフィールドのような感じで作れると嬉しいのですが...
(Seasar2のPropertyInterTypeみたいなことできないかなー?)

とりあえず、OValも、もっと何がどこまででいるか調査しないと...

【残タスク】
- API用のJSONフォーマット
- UserToolがおかしくなった
- cron/task処理結果ページのテンプレート
- Page関係の抽象クラス再考
--------
http://www.suz-lab.com

「魔法使い」レベルの技術力

スズキです。

> 充分に発達した科学技術は、魔法と見分けが付かない。
> Any sufficiently advanced technology is indistinguishable from magic.

「2001年宇宙の旅」で有名な"アーサー・C・クラーク"の言葉です。
http://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%BC%E3%82%B5%E3%83%BC%E3%83%BBC%E3%83%BB%E3%82%AF%E3%83%A9%E3%83%BC%E3%82%AF

来年(2010年)は自分の持っている(IT)スキル一つ一つを、
「魔法使い」レベルまで高めれるように努力できれば、と思っています。

「魔法使い」のイメージは、
今のところ「斜め上を行く」のさらに向こう側って感じでしょうか…

来年(2010年)も、僕はプログラマーです。

--------
http://www.suz-lab.com

2009年12月29日火曜日

mobylet & Guice & T2 & Velocity on GAE/J

スズキです。

T2で"@Form(resolverClass=OvalFormResolver.class)"みたいな事をしようとすると、
結局、DIコンテナが必要になり(SimpleContainerAdapterではダメ)、
GAE/JならGuiceってことで、
http://d.hatena.ne.jp/mobylet/20090716/1247742912
"mobylet & T2 & Velocity on GAE/J"にGuiceも追加してみました。
http://blog.suz-lab.com/2009/12/multidevicefiltermobylet-t2-velocity-on.html

まず、WEB-INF/libに下記のjarを追加です。

guice-2.0.jar
guice-servlet-2.0.jar
guiceadapter-0.6.2-ga.jar

そしてweb.xmlの記述ですが、
"multi"と"t2"の両フィルターの間に、"guice"フィルターが入るようにします。

また、"t2"フィルターの"t2.container.adapter"パラメータは
"org.t2framework.t2.adapter.GuiceAdapter"にしておきます。

最終的なweb.xmlはこんな感じです。

--------【web.xml】--------
...
<context-param>
  <param-name>t2.encoding</param-name>
  <param-value>UTF-8</param-value>
</context-param>

<filter>
  <filter-name>mobylet</filter-name>
  <filter-class>org.mobylet.core.http.MobyletFilter</filter-class>
  <init-param>
    <param-name>mobylet.config.dir</param-name>
    <param-value>WEB-INF/mobylet/</param-value>
  </init-param>
</filter>

<filter>
  <filter-name>multi</filter-name>
  <filter-class>suz.lab.gae.filter.MultiDeviceFilter</filter-class>
</filter>

<filter>
  <filter-name>guice</filter-name>
  <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>

<filter>
  <filter-name>t2</filter-name>
  <filter-class>org.t2framework.t2.filter.T2Filter</filter-class>
  <init-param>
    <param-name>t2.rootpackage</param-name>
    <param-value>suz.lab.feed.page</param-value>
  </init-param>
  <init-param>
    <param-name>t2.container.adapter</param-name>
    <param-value>org.t2framework.t2.adapter.GuiceAdapter</param-value>
  </init-param>
  <init-param>
    <param-name>t2.exclude-resources</param-name>
    <param-value>txt, css, js, ico</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>mobylet</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
  <filter-name>multi</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
  <filter-name>guice</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
  <filter-name>t2</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
  <servlet-name>velocity</servlet-name>
  <servlet-class>org.apache.velocity.tools.view.servlet.VelocityLayoutServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>velocity</servlet-name>
  <url-pattern>*.html</url-pattern>
</servlet-mapping>

--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/war/WEB-INF/web.xml?r=75

ようやく、「OValを使ったPageクラスでのバリデーション」の準備が整った…

【残タスク】
- OValを使ったPageクラスでのバリデーション
- API用のJSONフォーマット
- cron/task処理結果ページのテンプレート
- Page関係の抽象クラス再考

--------
http://www.suz-lab.com

"appengine ja night #4"に参加予定

スズキです。

久しぶりにIT系のイベントに参加することにしました。

「appengine ja night #4」です。
http://atnd.org/events/2698

GAE/Jのトランザクション関係は、一回しっかりと調査しておきたかったので、
グルージェント荒川さんの「App Engine Transaction Puzzler」が非常に楽しみです。

無事、参加できるように、アクシデントが起こらないことを祈ろう...

--------
http://www.suz-lab.com

Pageクラス関係のネーミングルール(T2)

スズキです。

今までは1クラス1HTMLしか想定していなかったので、
下記のように、該当HTMLの拡張子を除いたファイル名に"Page"つける形で
問題ありませんでした。

@Page("/other/index.html")
public class IndexPage {

次にJSON形式の"Web API"も想定する必要がでてきて、
結局、ページクラスはこんな感じにしました。

@Page("/other/index.json")
public class IndexJson {

まずURLの拡張子は、出力フォーマットを表すようにしています。
そしてクラス名は、該当URLの拡張子を除いたファイル名に
"拡張子名"つける形にしています。

ちなみに、"index.html"からの非同期通信(Ajax)"は
"index.json"に対して行うってことにしています。
複数の処理が必要な場合は、
"index.json/insert"、"index.json/update"といった感じでアクセスです。
(このあたりのハンドリングはT2ならお手のものです!)

とすると、最初に紹介したHTMLは、こんな感じになるはずです。

@Page("/other/index.html")
public class IndexHtml {

XMLなら、
@Page("/other/index.xml")
public class IndexXml {

CSVなら、
@Page("/other/index.csv")
public class IndexCsv {

...

まあ、また、変更する可能性は高いですが、今はこんなルールに落ち着いています。

--------
http://www.suz-lab.com

2009年12月28日月曜日

"jQuery & T2"でAjax

スズキです。

GAE/J系、いろいろやっていましたが、
そろそろ、フォームからの入力処理をかためておきたいところです。

フォームからの入力処理は、基本的に非同期通信(Ajax)で行おうと思っています。
そうなると経験的にJavascript側は、
jQuery( Form & Validation Plugin)の選択になります。
http://blog.suz-lab.com/2008/11/jquery-validate-jquery-form.html

コードは下記のような感じです。

簡単なURLを入力して登録するフォームです。
必須かつURLの形式になってないとエラーになります。
通信先は"/mypage/index.json/insert"としています。
--------【Velocity】--------
#set($layout="other/layout.html")
<script type="text/javascript" src="/other/js/ext/form.js"></script>
<script type="text/javascript" src="/other/js/ext/validate.js"></script>
<script type="text/javascript" src="/other/js/app/mypage/index.js"></script>
<h2>マイページ</h2>
<a href="$user.createLogoutURL("/index.html")">ログアウト</a>
<form id="form" method="post" action="/mypage/index.json/insert">
  <input type="text" name="url" class="required url" size="100"/><br />
  <input type="submit" value="登録"/><br />
</form>
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/war/WEB-INF/vm/page/other/mypage/index.html?r=70

上記のフォームにValidationプラグインとFormプラグインを適用させています。
帰ってくるデータはJSONを前提にし、成功したらalertで送信したURLが表示されます。
--------【Javascript】--------
$(function() {
  $("#form").validate({
    submitHandler: function(form) {
      $(form).ajaxSubmit({
        dataType: "json",
        success: function(json) {
          alert(json.url);
        }
      });
    }
  });
});
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/war/other/js/app/mypage/index.js?r=70


"/mypage/index.json/insert"に非同期通信かつPOSTでアクセスされたら、
引数(url)にPOSTで送られたパラメータ(url)の値が入った形でinsertメソッドが実行されます。
結果はJSONで出力するようにしています。
--------【Java】--------
@Page("/other/mypage/index.json")
public class IndexJson {
  @Ajax
  @POST
  @ActionPath
  public Navigation insert(
    @RequestParam("url") String url
  ) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("url", url);
    return Json.convert(map);
  }
}
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/src/suz/lab/feed/page/other/mypage/IndexJson.java?r=70

T2のアノテーションはいろいろと用意されているので、適材適所で使えるように、
下記を熟読しなければ…
▼T2機能
http://sites.google.com/site/t2tips/Home/t2-userguide/3-1-t2kinou
▼アノテーション
http://code.google.com/p/t-2/wiki/Annotation
▼T2 ユーザガイド
http://t-2.googlecode.com/files/T2_UserGuice_Japanese%28ver0.5%29.pdf

【残タスク】
- Pageクラスのネーミングルール
- Pageクラスでのバリデーション
- API用のJSONフォーマット
- cron/task処理結果ページのテンプレート
- Page関係の抽象クラス再考

--------
http://www.suz-lab.com

GAE/Jの"spin-down"対策(改)

スズキです。

以前、"cron"で定期的に"/"アクセスするようにしていたのですが、
http://blog.suz-lab.com/2009/12/gaejspin-down.html
以下のように、変更しました。

まず、1分毎に定期的に"/cron/keep_alive.html"にアクセスするようにしています。

--------【cron.xml】--------
...
<cron>
  <url>/cron/keep_alive.html</url>
  <description>Keep Alive</description>
  <schedule>every 1 minutes</schedule>
</cron>
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/war/WEB-INF/cron.xml?r=66

"keep_alive.html"はこんな感じです。

--------【Velocity】--------
#set($layout="other/cron/layout.html")
cron
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/war/WEB-INF/vm/page/other/cron/keep_alive.html?r=67
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/war/WEB-INF/vm/page/other/cron/layout.html?r=67

本当はもっと実行時の情報を出力するようにしたいのですが...

あと、/cron/*に関しては、下記のようなセキュリティ設定をしています。

--------【web.xml】--------
...
<security-constraint>
  <web-resource-collection>
    <web-resource-name>cron</web-resource-name>
    <url-pattern>/cron/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin</role-name>
  </auth-constraint>
</security-constraint>
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/war/WEB-INF/web.xml?r=69

"Task Queue"も同じような感じでやろう。

--------
http://www.suz-lab.com

2009年12月27日日曜日

MapTool(mobylet用VelocityTools)

スズキです。

今度はGoogleマップ関係のVelocityToolsです。
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/tool/MapTool.java?r=63

mobyletにGoogleマップ(static)を利用する機能があったので、
その(GoogleMapDesigner)ラッパーです。
https://www.seasar.org/svn/mobylet/trunk/mobylet-core/src/main/java/org/mobylet/view/designer/GoogleMapDesigner.java

利用するには、やはり"toolbox.xml"からです。

--------【toolbox.xml】--------

<tool>
  <key>map</key>
  <scope>request</scope>
  <class>suz.lab.gae.tool.MapTool</class>
  <parameter name="key" value="XXXXXXXXX"/>
</tool>
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/abokyu-apps/war/WEB-INF/toolbox.xml?r=64

Velocityテンプレートは、こんな感じです。

--------【Velocity】--------
#set($layout="mobile/layout.html")
<img src="$map.getSrc(35.6477196377186, 139.70972299575806, 17,
35.64841274910836, 139.7095862030983)"/>
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/abokyu-apps/war/WEB-INF/vm/page/mobile/access.html?r=64

上記の例は
第一引数: 中心緯度
第二引数: 中心経度
第三引数: 縮尺
第四引数: マーカー緯度
第五引数: マーカー経度
となっています。

実際の地図はこんな感じです。
http://maps.google.com/staticmap?maptype=mobile&center=35.6477196377186,139.70972299575806&zoom=17&size=240x270&sensor=false&markers=35.64841274910836,139.7095862030983

次は画像のリサイズ、やってみよう。

--------
http://www.suz-lab.com

2009年12月26日土曜日

UserTool(GAE/J用VelocityTools)

スズキです。

こんなVelocityToolsを作ってみました。
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/tool/UserTool.java?r=62

GAE/JのUserServiceのラッパーって感じです。
Pageクラスを作らず、Velocityテンプレートだけで、認証関係の処理が利用できます。
(どっちかというと以下の記事の続きです)
http://blog.suz-lab.com/2009/12/gaej_25.html

実際に下記URLで利用して見ました。
http://feed.suz-lab.com/mypage/index.html

以下のように表示されると思います。
【admin】 false
【loggedIn】 gmail.com
【email】 suzuki@suz-lab.com
【nickname】 suzuki@suz-lab.com
【userId】 000000000000000000000
【createLoginURL】
https://www.google.com/accounts/ServiceLogin?service=ah&continue=http://feed.suz-lab.com/_ah/login%3Fcontinue%3Dhttp://feed.suz-lab.com/mypage/index.html&ltmpl=gm&ahname=suz-lab-feed&sig=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
【createLogoutURL】
http://feed.suz-lab.com/_ah/logout?continue=https://www.google.com/accounts/Logout%3Fcontinue%3Dhttp://feed.suz-lab.com/index.html%26service%3Dah

利用するためには、"toolbox.xml"に、その旨、書く必要があります。

--------【toolbox.xml】--------
<?xml version="1.0"?>
<toolbox>
  <tool>
    <key>user</key>
    <scope>request</scope>
    <class>suz.lab.gae.tool.UserTool</class>
  </tool>
  …
</toolbox>
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/war/WEB-INF/toolbox.xml?r=62

すると、以下のように"$user"でUserToolが利用できます。

--------【Velocity】--------
#set($layout="other/layout.html")
<h2>マイページ</h2>
<a href="$user.createLogoutURL("/index.html")">ログアウト</a>
<dl>
  <dt>admin</dt>
  <dd>$user.admin</dd>
  <dt>loggedIn</dt>
  <dd>$user.authDomain</dd>
  <dt>email</dt>
  <dd>$user.email</dd>
  <dt>nickname</dt>
  <dd>$user.nickname</dd>
  <dt>userId</dt>
  <dd>$user.userId</dd>
  <dt>createLoginURL</dt>
  <dd>$user.createLoginURL("/mypage/index.html")</dd>
  <dt>createLogoutURL</dt>
  <dd>$user.createLogoutURL("/index.html")</dd>
</dl>
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/war/WEB-INF/vm/page/other/mypage/index.html?r=62

次は、MemcacheService、やろう。

--------
http://www.suz-lab.com

2009年12月25日金曜日

GAE/Jでログイン&ログアウト

スズキです。

GAE/J上での認証が必要なページの作り方です。

実物はこちら(http://feed.suz-lab.com/)です。

"マイページ"をクリックすると、ログインしてなければGoogleのログインページに遷移し、
ログインが成功したら、該当ページ(/mypage/index.html)に帰ってきます。
ログイン後のページには"ログアウト"があり、それをクリックすると、
一旦、Google側でログアウトして、ログアウト後のページ(/index.html)に遷移します。

下記の情報をもとに、実装した感じです。

▼セキュリティと認証
http://code.google.com/intl/ja/appengine/docs/java/config/webxml.html#Security_and_Authentication

▼APIリファレンス
http://code.google.com/intl/ja/appengine/docs/java/javadoc/com/google/appengine/api/users/package-summary.html

実際のコードは以下のようになっています。

--------【web.xml】--------
...
<security-constraint>
  <web-resource-collection>
    <web-resource-name>mypage</web-resource-name>
    <url-pattern>/mypage/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>*</role-name>
  </auth-constraint>
</security-constraint>
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/war/WEB-INF/web.xml?r=57

--------【Java】--------
@Page("/other/mypage")
public class IndexPage extends AbstractIndexPage {
  @Override
  protected void setAttributes(Request request) {
    UserService userService = UserServiceFactory.getUserService();
    request.setAttribute("logout", userService.createLogoutURL("/index.html"));
  }
}
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/src/suz/lab/feed/page/other/mypage/IndexPage.java?r=57

--------【Velocity】--------
#set($layout="other/layout.html")
<h2>マイページ</h2>
<a href="$logout">ログアウト</a>
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-feed/war/WEB-INF/vm/page/other/mypage/index.html?r=57

UserService系の処理はVelocityToolで使えるようにしたほうがよさそうだなー…

--------
http://www.suz-lab.com

2009年12月24日木曜日

フィード表示のサンプル(GAE/J)

スズキです。

"http://www.suz-lab.com/"でブログの記事のタイトル一覧を表示するようにしました。
下記のサンプルって感じです。
http://blog.suz-lab.com/2009/12/geajrome.html

Pageクラスでこんな処理をして、

--------【Java】--------
...
MemcacheService memcacheService = MemcacheServiceFactory.getMemcacheService();
SyndFeed feed = (SyndFeed)memcacheService.get("feed");
if(feed == null) {
  URLFetchService fetchService = URLFetchServiceFactory.getURLFetchService();
  HTTPRequest httpRequest = new HTTPRequest(
    new URL("http://feeds.feedburner.com/suz-lab-blog"),
    HTTPMethod.GET,
    disallowTruncate().followRedirects()
  );
  HTTPResponse httpResponse = fetchService.fetch(httpRequest);
  SyndFeedInput feedInput = new SyndFeedInput();
  feed = feedInput.build(
    new InputStreamReader(
      new ByteArrayInputStream(httpResponse.getContent()),
      "UTF-8"
    )
  );
  memcacheService.put("feed", feed, Expiration.byDeltaSeconds(3600));
}
request.setAttribute("feed", feed);
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/src/suz/lab/apps/page/other/IndexPage.java?r=53

以下のようなVelocityテンプレートを表示すると、

--------【Velocity】--------
...
<dl class="news">
  #foreach($entry in $feed.entries)
  <dt>$date.format("yyyy/MM/dd", $entry.publishedDate)</dt>
  <dd><a href="$entry.link">$entry.title</a></dd>
  #end
</dl>
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/war/WEB-INF/vm/page/other/index.html?r=53

次のようにタイトル一覧が表示されます。

http://www.suz-lab.com/

年末年始休みにフィードを扱ったサービスでもつくろう。

"mobylet + T2 + Velocity on GAE/J"のサンプル

スズキです。



今までいろいろ書いてきましたが、「百聞は一見にしかず」ということで、
見れるサンプルです。

ソース群はこちらです。
http://code.google.com/p/suz-lab-gae/source/browse/?r=52#svn/trunk/suz-lab-apps

実物はこちらです。
http://www.suz-lab.com/

PCでアクセスすると、
page.other.IndexPage.javaが実行され、vm/page/other/index.htmlが表示、
携帯でアクセスすると、
page.mobile.IndexPage.javaが実行され、vm/page/mobile/index.htmlが表示、
となります。

ようやく、"http://www.suz-lab.com/"を作りこんでいく基板ができた…

--------
http://www.suz-lab.com

T2で"/"でも"/index.html"の処理になるように(マルチデバイス対応)

スズキです。

下記のマルチデバイス対応したら、
http://blog.suz-lab.com/2009/12/multidevicefiltermobylet-t2-velocity-on.html
下記の"T2で"/"でも"/index.html"の処理"がおかしくなってしまいました。
http://blog.suz-lab.com/2009/12/t2indexhtml_21.html

page.mobile.IndexPage.java
page.other.IndexPage.java

を作成して、PCにて"/index.html"にアクセスすると、
本来なら、"page.other.IndexPage.java"呼ばれて欲しかったのですが、
"page.mobile.IndexPage.java"が呼ばれてしまいました。

なので、正常に動作するように修正です。

--------【AbstractIndexPage】--------
public abstract class AbstractIndexPage extends AbstractPage {
  @Override
  @Default
  public Navigation others(Request request) {
    String path = request.getRequestURI();
    if(path.endsWith("/")) {
      this.setAttributes(request);
      return Forward.to(path + "index.html");
    } else if(path.endsWith("/index.html")) {
      this.setAttributes(request);
      return Forward.to(path);
    } else {
      return Forward.to(path);
    }
  }
}
--------

-------【mobile.IndexPage】-------
@Page("/mobile")
public class IndexPage extends AbstractIndexPage {
  @Override
  protected void setAttributes(Request request) {
    ...
  }
}
--------

-------【other.IndexPage】-------
@Page("/other")
public class IndexPage extends AbstractIndexPage {
  @Override
  protected void setAttributes(Request request) {
    ...
  }
}
--------

要は、親の抽象クラスに@Page("/")と入れていたアノテーションを、
子供に@Page("/other")と入れた感じです。

だんだん、場当たりになってきた…

--------
http://www.suz-lab.com

2009年12月22日火曜日

MultiDeviceFilter(mobylet + T2 + Velocity on GAE/J)

スズキです。

GAE/J上で"/index.html"などの同じURLで、PCからのアクセスと携帯からのアクセスで
表示内容を変えたいなーと思い、mobylet(携帯専用フレームワーク)、T2、Velocity、
そして独自フィルターを用いて実現してみました。(それぞれのサイトは下記となります)

▼mobylet
http://mobylet.seasar.org/
▼T2
http://code.google.com/p/t-2/
▼Velocity(VelocityView)
http://velocity.apache.org/tools/devel/view.html
▼独自フィルター
http://code.google.com/p/suz-lab-gae/source/browse/?r=40#svn/trunk/suz-lab-gae/src/suz/lab/gae/filter

"web.xml"は以下のようになりました。

最初に"mobylet"フィルター、次が独自フィルター、
そして、"T2"フィルター、"Velocity"サーブレットと続いています。

--------【web.xml】--------
...
<context-param>
  <param-name>t2.encoding</param-name>
  <param-value>UTF-8</param-value>
</context-param>

<filter>
  <filter-name>mobylet</filter-name>
  <filter-class>org.mobylet.core.http.MobyletFilter</filter-class>
  <init-param>
    <param-name>mobylet.config.dir</param-name>
    <param-value>WEB-INF/mobylet/</param-value>
  </init-param>
</filter>

<filter>
  <filter-name>multi</filter-name>
  <filter-class>suz.lab.gae.filter.MultiDeviceFilter</filter-class>
</filter>

<filter>
  <filter-name>t2</filter-name>
  <filter-class>org.t2framework.t2.filter.T2Filter</filter-class>
  <init-param>
    <param-name>t2.rootpackage</param-name>
    <param-value>abokyu.apps.page</param-value>
  </init-param>
  <init-param>
    <param-name>t2.container.adapter</param-name>
    <param-value>org.t2framework.t2.adapter.SimpleContainerAdapter</param-value>
  </init-param>
  <init-param>
    <param-name>t2.exclude-resources</param-name>
    <param-value>txt, css, js, ico</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>mobylet</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
  <filter-name>multi</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
  <filter-name>t2</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
  <servlet-name>velocity</servlet-name>
  <servlet-class>org.apache.velocity.tools.view.servlet.VelocityLayoutServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>velocity</servlet-name>
  <url-pattern>*.html</url-pattern>
</servlet-mapping>
...
--------

"mobylet"に関してですが、携帯端末以外は変換処理がかからないように、
"WEB-INF/mobylet/mobylet.xml"を下記のようにしています。

--------【mobylet.xml】--------
...
<through>
  <carrier>OTHER</carrier>
</through>
...
--------

そして、独自フィルターです。コードは下記のようになっていて、
まず、"response.setCharacterEncoding("UTF-8")"をしています。
これは、"mobylet"フィルターをかけると"ISO-8859-1"が設定されてしまい、
Velocity変換時に文字化けしてしまうからです。

--------【MultiDeviceFilter】--------
...
@Override
public void doFilter(
    ServletRequest request,
    ServletResponse response,
    FilterChain chain
) throws IOException, ServletException {
  response.setCharacterEncoding("UTF-8");
  chain.doFilter(new MultiDeviceHttpServletRequest(
    (HttpServletRequest)request), response
  );
}
...
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/filter/MultiDeviceFilter.java?r=40

次に以下の"MultiDeviceHttpServletRequest"を使って、
デバイス(mobile/other)ごとに"RequestURI"と"ServletPath"を調整しています。

--------【MultiDeviceFilter】--------
...
public class MultiDeviceHttpServletRequest extends HttpServletRequestWrapper {
  private String device;

  public MultiDeviceHttpServletRequest(HttpServletRequest request)
      throws UnsupportedEncodingException {
    super(request);
    Carrier carrier = MobyletFactory.getInstance().getCarrier();
    if(carrier == Carrier.OTHER) {
      this.device = "other";
    } else {
      this.device = "mobile";
    }
  }

  @Override
  public String getRequestURI() {
    if(super.getRequestURI().startsWith("/" + this.device)) {
      return super.getRequestURI();
    } else {
      return "/" + this.device + super.getRequestURI();
    }
  }

  @Override
  public String getServletPath() {
    if(super.getServletPath().startsWith("/" + this.device)) {
      return super.getServletPath();
    } else {
      return "/" + this.device + super.getServletPath();
    }
  }
}
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/filter/MultiDeviceHttpServletRequest.java?r=40

こうすることで、"/index.html"にアクセスした場合、
PCだと、"page.other.IndexPage.java(T2)"が実行され、
テンプレートは"vm/page/other/index.html(Velocity)"が利用され、
携帯だと、"page.mobile.IndexPage.java(T2)"が実行され、
テンプレートは"vm/page/mobile/index.html(Velocity)"が利用され、
さらに、"mobylet"のフィルター効果もききます。

ってダラダラ書いてしまいましたが、ブランクプロジェクト作れって話ですね。
実際に動いてるものと、そのソースも公開したい…

--------
http://www.suz-lab.com

2009年12月21日月曜日

T2で"/"でも"/index.html"の処理になるように(サブフォルダ対応)

スズキです。
「T2で"/"でも"/index.html"の処理になるように」を改良してみました。
http://blog.suz-lab.com/2009/12/t2indexhtml.html
今までのものは、"/mobile/"などのサブフォルダに対応していなかったので、
それにも対応するよう、下記のように修正しています。
--------【Java】--------
@Page("/")
public abstract class AbstractIndexPage extends AbstractPage {
  @Override
  @Default
  public Navigation others(Request request) {
    String path = request.getRequestURI();
    if(path.endsWith("/")) {
      this.setAttributes(request);
      return Forward.to(path + "index.html");
    } else if(path.endsWith("/index.html")) {
      this.setAttributes(request);
      return Forward.to(path);
    } else {
      return Forward.to(path);
    }
  }
}
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/page/AbstractIndexPage.java?r=36

"mobylet + T2 + Velocity on GAE/J"がとりあえず動くようになった!
--------
http://www.suz-lab.com

2009年12月18日金曜日

T2で"/"でも"/index.html"の処理になるように

スズキです。

いろいろやり方はあると思いますが、
とりあえず、下記のような感じで実現してみました。

まずはPageの抽象クラスです。
結局、"request.setAttriburte"なので、
それが目的の抽象メソッド作っときました。

--------【AbstractPage】--------
public abstract class AbstractPage {
  // "@Default"で実行される処理を想定
  public abstract Navigation others(Request request);
  // テンプレートに渡すオブジェクトをセット
  protected abstract void setAttributes(Request request);
}
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/page/AbstractPage.java?r=34

TOPページ("/"と"/index.html"でアクセス)は下記のクラスを継承します。

--------【AbstractIndexPage】--------
@Page("/")
public abstract class AbstractIndexPage extends AbstractPage {
  // "/"か"index.html"の時は"/index.html"にフォワード
  @Override
  @Default
  public Navigation others(Request request) {
    String path = request.getRequestURI();
    if(path.equals("/") || path.equals("/index.html")) {
      this.setAttributes(request);
      return Forward.to("/index.html");
    } else {
      return Forward.to(request.getRequestURI());
    }
  }
}
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/page/AbstractIndexPage.java?r=34

TOPページ以外は下記を継承です。

--------【AbstractDefaultPage】--------
public abstract class AbstractDefaultPage extends AbstractPage {
  @Override
  @Default
  public Navigation others(Request request) {
    this.setAttributes(request);
    return Forward.to(request.getRequestURI());
  }
}
--------
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-gae/src/suz/lab/gae/page/AbstractDefaultPage.java?r=34

で、実際の"index.html"のPageクラスは下記のように継承して作成します。

--------【IndexPage】--------
public class IndexPage extends AbstractIndexPage {
  @Override
  protected void setAttributes(Request request) {
    request.setAttribute("hoge", "hoge");
  }
}
--------

すると"/"と"/index.html"のどちらでアクセスしても、上記の"setAttributes"が実行され、
テンプレート(index.html)が表示します。

そろそろ、T2のプラグイン、やってみよう。

--------
http://www.suz-lab.com

2009年12月17日木曜日

iconvでファイルの文字コード変換(SHIFT_JIS→UTF-8)

スズキです。

サーバ上で作業しているときに、
文字コード変換したくなること、あると思います。

さすがに、ダウンロードして、ローカルで変換して、
アップロードってのは効率悪すぎです。

そういうときは、下記のようにサーバ上のコマンドが利用できます。

# iconv -f SHIFT_JIS -t UTF-8 shift-jis.txt > utf-8.txt

だいたいの環境でiconvは入ってるんじゃいでしょうか?

--------
http://www.suz-lab.com

64 ビット対応版 Google 日本語入力リリース

スズキです。

ようやく、リリースされました。
http://googlejapan.blogspot.com/2009/12/64-google.html

MacとWinで同じIMEが無料で使えるようになったのが大きいです。

--------
http://www.suz-lab.com

GAE/JのMemcacheでフィードをキャッシュ

スズキです。

下記記事で、GAE/J上でのフィードの取得&表示までできました。
http://blog.suz-lab.com/2009/12/geajrome.html

しかし、リクエストの都度フィードを取得するのはよろしくないので、
Memcacheにキャッシュするようにしてみました。

--------【Java】--------
MemcacheService memcacheService = MemcacheServiceFactory.getMemcacheService();
SyndFeed feed = (SyndFeed)memcacheService.get("feed");
if(feed == null) {
  URLFetchService fetchService = URLFetchServiceFactory.getURLFetchService();
  HTTPRequest httpRequest = new HTTPRequest(
    new URL("http://feeds.feedburner.com/suz-lab-blog"),
    HTTPMethod.GET,
    disallowTruncate().followRedirects()
  );
  HTTPResponse httpResponse = fetchService.fetch(httpRequest);
  SyndFeedInput feedInput = new SyndFeedInput();
  feed = feedInput.build(
    new InputStreamReader(
      new ByteArrayInputStream(httpResponse.getContent()),
      "UTF-8"
    )
  );
  memcacheService.put("feed", feed, Expiration.byDeltaSeconds(3600));
}
request.setAttribute("feed", feed);
--------

Memcacheにフィードがなかったら、該当URLにフィードを取得しにいきます。
MemcacheのExpireは1日(3600秒)にしています。

最終的にはフィードはcronで定期的に、取得&Datastoreに保存して、
Memcacheにフィードが無かったらDatastoreからフィードを取得する形にしたいなー。

--------
http://www.suz-lab.com

VelocityViewでtoolbox.xmlを設定

スズキです。

VelocityView(VelocityViewServle/VelocityLayoutServlet)では、
http://blog.suz-lab.com/2009/12/gaejvelocity.html
http://blog.suz-lab.com/2009/12/gaejvelocitylayoutservlet.html
以下のように、toolbox.xmlを設定することにより、
テンプレート上で便利クラスを利用することができます。
(詳細は書きURLの"Toolbox Configuration"のところです)
http://velocity.apache.org/tools/releases/1.4/view/

--------【WEB-INF/toolbox.xml】--------
<?xml version="1.0"?>
<toolbox>
  <tool>
    <key>date</key>
    <scope>application</scope>
    <class>org.apache.velocity.tools.generic.ComparisonDateTool</class>
  </tool>
</toolbox>
--------

上記を利用したVelocityテンプレートは以下のようになります。

--------【Velocity】--------
<dl>
  #foreach($entry in $feed.entries)
  <dt>$date.format("yyyy/MM/dd", $entry.publishedDate)</dt>
  <dd><a href="$entry.link">$entry.title</a></dd>
  #end
</dl>
--------

今回は、テンプレート上で日付をフォーマットしたかったので、
DateTool(ComparisonDateTool)を利用してみました。
(DateToolに関する詳細はこちらです)
http://velocity.apache.org/tools/releases/1.4/javadoc/org/apache/velocity/tools/generic/DateTool.html

※"WEB-INF/lib"に下記jarが必要です。
commons-beanutils-1.7.0.jar
velocity-tools-generic-1.4.jar

他にも以下で紹介されているように、いろいろなToolが用意されています。
http://velocity.apache.org/tools/releases/1.4/generic/
http://velocity.apache.org/tools/releases/1.4/javadoc/org/apache/velocity/tools/view/tools/package-summary.html

AbstractPagerToolとか面白そうだなー。
  

GEA/J上でROMEを使ってフィード処理

スズキです。

GAE/Jで「適当なフィードをを読み込み表示する」ってやつの続きです。
前回の「GAE/JでURLフェッチの低レベルAPI」でフィードの取得まではできました。
http://blog.suz-lab.com/2009/12/gaejurlapi.html

次はROME 使って、
https://rome.dev.java.net/
フィード情報をオブジェクトにして、Velocityテンプレートに渡して表示です。

まず、ROMEを使うには下記のjarが必要になります。

--------【WEB-INF/lib】--------
...
jdom-1.1.1.jar
rome-1.0.jar
xercesImpl-2.9.1.jar
--------

そして、下記コードで、URLフェッチで取得したフィードデータを
ROMEのオブジェクトにしてVelocityテンプレートに渡しています。
("T2 & Velocity"を前提にしています)
http://blog.suz-lab.com/2009/12/gaej-velocity-t2.html

--------【Java】--------
URLFetchService fetchService = URLFetchServiceFactory.getURLFetchService();
HTTPRequest httpRequest = new HTTPRequest(
  new URL("http://feeds.feedburner.com/suz-lab-blog"),
  HTTPMethod.GET,
  disallowTruncate().followRedirects()
);
HTTPResponse httpResponse = fetchService.fetch(httpRequest);
SyndFeedInput feedInput = new SyndFeedInput();
SyndFeed feed = feedInput.build(
  new InputStreamReader(
    new ByteArrayInputStream(httpResponse.getContent()),
    "UTF-8"
  )
);
request.setAttribute("feed", feed);
--------

InputStreamReaderをインスタンス化するときに、
第二引数に文字コードを指定しないと、文字化けします。

Velocityテンプレートは次のような感じです。

--------【Velocity】--------
<dl class="news">
  #foreach($entry in $feed.entries)
  <dt>$entry.publishedDate</dt>
  <dd><a href="$entry.link">$entry.title</a></dd>
  #end
</dl>
--------

このままだと、日付の表記がカッコ悪いので、"toolbox.xml"を設定しなければ...

2009年12月16日水曜日

GAE/Jの"spin-down"対策

スズキです。

べたですが、下記のように、cronで一分おきにアクセスするようにしました。

--------【cron.xml】--------
<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/</url>
    <description>Keep Alive</description>
    <schedule>every 1 minutes</schedule>
  </cron>
</cronentries>
--------

cronの詳細はこちら。
http://code.google.com/intl/ja/appengine/docs/java/config/cron.html

VelocityやT2の設定で、spin-upの時間、短縮できないかなー?

--------
http://www.suz-lab.com

GAE/JでURLフェッチの低レベルAPI

スズキです。

GAE/Jで「適当なフィードをを読み込み表示する」ってのをやろうとして、
まずはURLフェッチの低レベルAPIを試してみました。
http://code.google.com/intl/ja/appengine/docs/java/javadoc/com/google/appengine/api/urlfetch/package-summary.html

ソースはこんな感じになります。

--------【Java】--------
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
import static com.google.appengine.api.urlfetch.FetchOptions.Builder.disallowTruncate;
...
URLFetchService fetchService = URLFetchServiceFactory.getURLFetchService();
HTTPRequest httpRequest = new HTTPRequest(
  new URL("http://feeds.feedburner.com/suz-lab-blog"),
  HTTPMethod.GET,
  disallowTruncate().followRedirects()
);
HTTPResponse httpResponse = fetchService.fetch(httpRequest);
...
--------

上記コードで"httpResponse"から
指定したURLのヘッダやコンテンツを取得することができます。
http://code.google.com/intl/ja/appengine/docs/java/javadoc/com/google/appengine/api/urlfetch/HTTPResponse.html

HTTPRequestの第三引数のオプションは下記を参考に。
http://code.google.com/intl/ja/appengine/docs/java/javadoc/com/google/appengine/api/urlfetch/FetchOptions.html

次はフィードの処理です。

--------
http://www.suz-lab.com

「おおたに6号機blog」に紹介されました!

スズキです。

T2のコミッタであるshot6さんのブログ「おおたに6号機blog」に
"GAE/J & Velocity & T2"の記事が紹介されました。
http://d.hatena.ne.jp/shot6/20091214#1260750152

やはり、コミッタの方にかまっていただくと、
モチベーションがあがります。

がんばろう。

--------
http://www.suz-lab.com

2009年12月15日火曜日

"Amazon Web Services"の障害情報

スズキです。

下記より、確認できます。
http://status.aws.amazon.com/

フィードも提供されているので、利用しているサービスがあったら、
該当フィードも購読しておいたほうがいいかもしれません。

"2009/12/09"に
"Amazon Elastic Compute Cloud (US - N. Virginia)"で障害があったのか...

--------
http://www.suz-lab.com

2009年12月11日金曜日

コンソールからPHPを実行するときphp.iniの値を指定

スズキです。

下記のように、"-d"オプションを利用します。

php -d short_open_tag=on -d memory_limit=1024M main.php

その他のオプションに関しては、下記が参考になります。

http://it.kndb.jp/entry/show/id/1064

バッチで"fetchAll"は危険...

--------
http://www.suz-lab.com

GAE/J & Velocity & T2

スズキです。

ようやく、やりたいことができました。

下記で、"/test.html"でアクセスしたら、TestPage.javaでセットされたパラメータが、
Velocityテンプレートに渡されて、パラメータが差し込まれた結果が表示されるってやつです。

必要最小限の"jar"は下記のような感じになります。

--------【WEB-INF/lib】-------
appengine-api-1.0-sdk-1.2.8.jar
appengine-api-labs-1.2.8.jar
commons-0.6.5-ga.jar
commons-collections-3.2.jar
commons-digester-1.8.jar
commons-lang-2.2.jar
commons-logging-1.1.jar
datanucleus-appengine-1.0.4.final.jar
datanucleus-core-1.1.5.jar
datanucleus-jpa-1.1.5.jar
geronimo-jpa_3.0_spec-1.1.1.jar
geronimo-jta_1.1_spec-1.1.1.jar
jdo2-api-2.3-eb.jar
slf4j-api-1.5.6.jar
slf4j-jcl-1.5.6.jar
t2-0.6.2-ga.jar
velocity-1.5.jar
velocity-tools-view-1.4.jar
--------

web.xmlは以下のようにします。
".html"の拡張子が、t2、velocityにかかるようにしています。

--------【WEB-INF/web.xml】-------
...
<context-param>
  <param-name>t2.encoding</param-name>
  <param-value>UTF-8</param-value>
</context-param>
<filter>
  <filter-name>t2</filter-name>
  <filter-class>org.t2framework.t2.filter.T2Filter</filter-class>
  <init-param>
    <param-name>t2.rootpackage</param-name>
    <param-value>suz.lab.page</param-value>
  </init-param>
  <init-param>
    <param-name>t2.container.adapter</param-name>
    <param-value>org.t2framework.t2.adapter.SimpleContainerAdapter</param-value>
  </init-param>
  <init-param>
    <param-name>t2.eagerload</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param>
    <param-name>t2.exclude-resources</param-name>
    <param-value>css, js, ico</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>t2</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
  <servlet-name>velocity</servlet-name>
  <servlet-class>org.apache.velocity.tools.view.servlet.VelocityLayoutServlet</servlet-class>
  <init-param>
    <param-name>org.apache.velocity.properties</param-name>
    <param-value>/WEB-INF/velocity.properties</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>velocity</servlet-name>
  <url-pattern>*.html</url-pattern>
</servlet-mapping>
<welcome-file-list>
  <welcome-file>index.html</welcome-file>
</welcome-file-list>
...
--------

"velocity.properties"はレイアウト機能使うってことで。

--------【WEB-INF/velocity.properties】--------
input.encoding=UTF-8
output.encoding=UTF-8
resource.loader = file
file.resource.loader.class=org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path=WEB-INF/vm/page
tools.view.servlet.layout.directory=/
tools.view.servlet.layout.default.template=layout.html
--------

t2関係のコード(Java)は"test:hoge"をセットして、
test.html(velocity)にフォワードしています。

--------【suz.lab.page.TestPage.java】--------
...
@Page("test.html")
public class TestPage {
  @Default
    public Navigation index(Request request) {
      request.setAttribute("test", "hoge");
      return Forward.to("/test.html");
  }
}
--------

テンプレートはt2から渡されたパラメータ(test)の内容を表示するだけです。

--------【WEB-INF/vm/page/test.html】--------
$test
--------

次は、フィード読み込んで表示するやつ作ろう。

--------
http://www.suz-lab.com

ServletからVMに(Velocity)にパラメータを渡す

スズキです。

このあたりができてるって前提です。
http://blog.suz-lab.com/2009/12/gaejvelocity.html

Servletのフォワードで簡単に実現できました。

--------【Servlet】--------
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
  req.setAttribute("test", "hoge");
  String dispatch = "/test.html";
  RequestDispatcher dispatcher = req.getRequestDispatcher(dispatch);
  dispatcher.forward(req, resp);
}
--------

--------【test.html】--------
$test
--------

上記のコードで、Servletがマッピングされている"/test"にアクセスすると、
"test:hoge"がtest.html(Velocity)に渡されて、"hoge" と表示されるはずです。

ってことで、そのままT2と連動できることがわかりました。

次は、T2連携だ!

--------
http://www.suz-lab.com

2009年12月10日木曜日

ポータブルアプリ、いろいろアップデート

スズキです。

近頃サボってたので、一気にアップデートです。

▼XnView Portable
http://portableapps.com/news/2009-12-06_-_xnview_portable_1.97

▼µTorrent Portable
http://portableapps.com/news/2009-12-06_-_utorrent_portable_1.8.5.17414

▼Sumatra PDF Portable
http://portableapps.com/news/2009-12-06_-_sumatra_pdf_portable_1.0.1

▼Inkscape Portable
http://portableapps.com/news/2009-12-04_-_inkscape_portable_0.47

▼Pidgin Portable
http://portableapps.com/news/2009-11-30_-_pidgin_portable_2.6.4

ポータブルの次は、いかにクリック数を減らすかだなー...

--------
http://www.suz-lab.com

GAE/JでVelocityLayoutServlet

スズキです。

こちらで、GAE/JにてVelocityが使えるようになったので、
http://blog.suz-lab.com/2009/12/gaejvelocity.html
今度は、レイアウト機能が使える、VelocityLayoutServletに挑戦です。
http://velocity.apache.org/tools/releases/1.4/view/layoutservlet.html

まずは、web.xmlを下記のように変更です。

--------【WEB-INF/web.xml】--------
<servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class>

<servlet-class>org.apache.velocity.tools.view.servlet.VelocityLayoutServlet</servlet-class>
--------

次にvelocity.propertiesに下記を追記です。

--------【WEB-INF/velocity.properties】--------
...
tools.view.servlet.layout.directory=/
tools.view.servlet.layout.default.template=layout.html
--------

"tools.view.servlet.layout.directory"はレイアウトのテンプレート置き場を指定します。
指定方法は"file.resource.loader.path"からの相対パスとなり、
上記は"file.resource.loader.path"と同じ場所を指定していることになります。

"tools.view.servlet.layout.default.template"は
デフォルトで適用されるレイアウトのテンプレートです。

※ URLの拡張子をhtmlにしたかったので、VMの拡張子はhtmlとしています。
※ サーブレットマッピングも下記のようにhtml拡張子に対して行っています。
<servlet-mapping>
  <servlet-name>velocity</servlet-name>
  <url-pattern>*.html</url-pattern>
</servlet-mapping>


そして、layout.htmlは下記のように作成します。

--------【WEB-INF/vm/page/layout.html】--------
<html>
  <body>
    <div id="wrapper">
      <div id="header">#parse("header.html")</div>
      <div id="content">$screen_content</div>
      <div id="footer">#parse("footer.html")</div>
    </div>
  </body>
</html>
--------

※ #parse(...)外部のテンプレートを取り込めます。
※ $screen_contentに実際にURLで指定だれたコンテンツが入ります。

次は、いよいよT2との組み合わせです。

--------
http://www.suz-lab.com

2009年12月9日水曜日

GAE/JでVelocity

スズキです。

久しぶりのブログです。(怠けてました...)

結局メールのテンプレートはVelocity使っちゃうので、
だったら、Webページのテンプレートも同じがいいなー、と思い、
GAE/JでVelocityを使うために、いろいろ調査してみました。
(最終的にはT2と連動するとこまでもってきたいです)

まずは最小構成ということで、下記のjarを用意します。

--------【WEB-INF/lib/】-------
commons-collections-3.2.jar
commons-digester-1.8.jar
commons-lang-2.2.jar
commons-logging-1.1.jar
velocity-1.5.jar
velocity-tools-view-1.4.jar
--------

今回は、VelocityViewを使うので、下記のようにweb.xmlを記述します。

--------【WEB-INF/web.xml】-------
...
<servlet>
  <servlet-name>velocity</servlet-name>
  <servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class>
  <init-param>
    <param-name>org.apache.velocity.properties</param-name>
    <param-value>/WEB-INF/velocity.properties</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>velocity</servlet-name>
  <url-pattern>*.vm</url-pattern>
</servlet-mapping>
...
--------

velocity.propertiesが必要になるので、下記のように用意します。

--------【WEB-INF/velocity.properties】-------
input.encoding=UTF-8
output.encoding=UTF-8
resource.loader = file
file.resource.loader.class=org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path=WEB-INF/vm/page
--------

すると、"http://localhost:8080/index.vm"が、
"WEB-INF/vm/page/index.vm"をテンプレートとしたページとして出力されます。

VelocityLayoutServletも、確認しておくか...
http://velocity.apache.org/tools/releases/1.4/view/layoutservlet.html

--------
http://www.suz-lab.com