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

2009年11月30日月曜日

2009年11月26日木曜日

マウスオーバーで拡大画像を表示(jQuery)

スズキです。

こちらのjQueryツールチッププラグインを利用すると簡単にできます。
http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/

こちらのデモの"An image with a tooltip"がまさにそれって感じです。
http://jquery.bassistance.de/tooltip/demo/

--------【HTML】--------
<link rel="stylesheet" href="/css/tooltip.css" />
<script type="text/javascript" language="javascript"
src="/js/jquery.js"></script>
<script type="text/javascript" language="javascript"
src="/js/tooltip.js"></script>
...
$('#image').tooltip({
  delay: 0,
  showURL: false,
  bodyHandler: function() {
    return $("<img/>").attr("src", "img/suz_large.jpg");
  }
});
...
<img id="image" src="img/suz_small.jpg" />
--------

CSSの読み込みを忘れないように...

いろんなことが中途半端だ...

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

2009年11月24日火曜日

PHPでディレクトリの中のディレクトリを出力

スズキです。

こんな感じです。

--------【PHP】--------
$base_path = "/home/suz";
$base_dir = opendir($base_path);
while($tmp = readdir($base_dir)) {
  if($tmp != ".." && $tmp != "." && is_dir($base_path . "/" . $tmp)) {
    print($tmp);
  }
}
closedir($base_dir);
--------

次は、久しぶりのImageMagickだ。

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

PushGmailでPOP通知(iPhone)

スズキです。

よく、GmailをiPhoneで見るのですが、
どうせなら、メールが来たらPOP通知してくれないかなー、と思い、
いろいろと調べてみたら、PushGmailというものを発見しました。

有料アプリ(115円)ですが...

受信トレイに入ってきたメールがPOP通知されます。
ですので、POP通知したくないメールとかは、Gmailのフィルタ機能などで、
調整する必要があります。

実際に使ってみると、現在、GmailでGTD的なことを試しており、
それとも相性抜群で、いきなり重宝するようになってしまいました。

こちらにも載せておこう...
http://jbbs.livedoor.jp/bbs/read.cgi/computer/41921/1235187679/

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

HTMLのテキストフォームでENTER押して...

スズキです。

HTMLの検索フォームで、テキストボックスに文字を入力して、
ENTER押したら検索結果表示して欲しいってのがあったので、
jQuery使って実装してみました。
(今までは検索ボタン(画像)押したら検索でした...)

--------【HTML】-------
...
<script language="javascript">
$(function(){
  $("#search").keypress(function(e) {
    if($("#search").val() != "" && e.which == 13) {
      // 検索結果表示処理
    }
  });
})
</script>
...
<form action="javascript:void(0)">
  <input type="text" id="search"/>
</form>
...
--------

まあ、ENTERキー(13)押したら...って処理ですね。
formタグのactiono属性を"javascript:void(0)"にしておくのもポイントでしょうか...

最近、いろいろと、ペースが落ちてる...

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

2009年11月19日木曜日

TomcatのContextパラメータのreloadableの対象はクラスのみ

スズキです。

こんなやつの話です。

<Context path="/examples" docBase="examples" reloadable="true">
...
</Context>

propertiesなどのファイルのみ更新してもリロードはされません。
なので、反映したいときは、適当なクラスファイルも更新させる必要があります。

ブログのネタになる調査は、ポジティブにいこう...

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

"Jiemamy API"で列の作成(型編)

スズキです。

こちらの続きです。
http://blog.suz-lab.com/2009/11/jiemamy-api.html

このスプレッドシートに対して、
http://spreadsheets.google.com/pub?key=tWC2LhgkL2-xZIb1Wpc9b6w&gid=0

こんな感じに実装すると、
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/src/suz/lab/apps/page/sandbox/DownloadPage.java?r=30

こんな感じになります。
http://www.suz-lab.com/sandbox/download

とりあえず、

int(11)

DataTypeCategory: INTEGER
SizedDataTypeAdapter: 11

char(3)

DataTypeCategory: CHARACTER
SizedDataTypeAdapter: 3

varchar(255)

DataTypeCategory: VARCHAR
SizedDataTypeAdapter: 255

といった感じにしています。

型自体の設定は直感的でしたが、サイズの指定とかで、Adapterを利用するなどは、
ちょっと調べないとわからないかもしれません。

とりあえず、実行できるDDLになったぞ!

次は、非公開スプレッドシートも認証とAPI使ってDDL作れるかだなー...

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

心が変われば、... 人生が変わる。

スズキです。


> 心が変われば、態度が変わる。
> 態度が変われば、行動が変わる。
> 行動が変われば、習慣が変わる。
> 習慣が変われば、人格が変わる。
> 人格が変われば、運命が変わる。
> 運命が変われば、人生が変わる。

ヒンズー教の教えのようです。
http://meigen.shiawasehp.net/prov/019.html

「コンバージョンレート」はどれくらいなんだろう?

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

2009年11月18日水曜日

"Jiemamy API"で列の作成

スズキです。

こちらの続きです。
http://blog.suz-lab.com/2009/11/google-spreadsheet-poi-jiemamy.html

テーブル(だけ)定義までのなんちゃってCREATE文まで作ったので、
今度は列定義です。

こちらを参考に、
http://docs.jiemamy.org/release/0.2.0/api-quickstart/htmlsingle/api-quickstart.html#d0e163

こんなソースで、
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/src/suz/lab/apps/page/sandbox/DownloadPage.java?r=29

こんな結果になるように作ってみました。
http://www.suz-lab.com/sandbox/download

でも、型指定がまだまだです...
(Spredsheetのフォーマットをどうするか?)
(文字型の桁数などはどう指定するか?)

難易度が高くなってきたぞ...

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

組織について

スズキです。


> 我々の間には「チームプレイ」などという都合のいい言い訳は存在せん。
> あるとすれば、スタンドプレーから生じる「チームワーク」だけだ。

いまさらですが、攻殻機動隊の「公安9課課長 荒巻大輔」の言葉です。

今までいろいろ考えてきたけど、この言葉に落ち着いてしまった...

ってことは、大規模な組織運営を
自分のビジョンからはずしてしまったってことなんだよねー...

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

2009年11月13日金曜日

"Google Spreadsheet" → "POI" → "Jiemamy" → "blancoSqlFormatter" → "SQL"

スズキです。

これや、
http://blog.suz-lab.com/2009/11/google-docsspreadsheet.html
これや、
http://blog.suz-lab.com/2009/11/gae-t2-poigoogle-spredsheet.html
これの流れです。
http://blog.suz-lab.com/2009/11/blancosqlformattersqljava.html

で、下記の"Google Spreadsheet"から、
http://spreadsheets.google.com/pub?key=tWC2LhgkL2-xZIb1Wpc9b6w&gid=0

DDLを生成するサンプルプログラムをGAE/Jで作ってみました。
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/src/suz/lab/apps/page/sandbox/DownloadPage.java?r=28
(でも、まだ、列の定義まではできていません...)

Jiemamyは下記のようにAPIがしっかりと定義され、ドキュメント化もされていたので、
http://docs.jiemamy.org/release/0.2.0/api-quickstart/htmlsingle/api-quickstart.html
かなり助かりました。

週末に、多少実用的なものにしよう。

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

"iknow"が"smart.fm"になってた

スズキです。

昔、英語の勉強で"iknow"ってことで記事書いたのですが、
http://blog.suz-lab.com/2008/04/iknow.html
三日坊主で利用しなくなり、すっかり忘れていたのですが...

メールを整理していると、"smart.fm"というよくわからんところからメールがあり、
サイト見てみたら、"iknow"っぽくて、もしや、と思って、調べてみたら...

"iknow"が"smart.fm"になってたってことでした。

せっかくだから、学習はじめるか... 英語じゃなくて、中国語を...

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

2009年11月12日木曜日

BlancoSqlFormatterでSQLを整形(Java)

スズキです。

後で使うと思うので、使い方だけメモ。

--------【Java】--------
BlancoSqlFormatter formatter = new BlancoSqlFormatter(new BlancoSqlRule());
String before = "select * from SUZ_LAB";
String after = formatter.format(before);
--------

Jiemamyを研究しなければ...

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

2009年11月10日火曜日

mysqlbinlogで日時指定

スズキです。

下記となります。

# mysqlbinlog --start-datetime="2009-11-10 07:22:37" mysqld-bin.log

他にも下記にいろいろ紹介されています。

http://beausoir.blog100.fc2.com/blog-entry-86.html

とりあえず、MySQLモード終了。

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

2009年11月9日月曜日

"Servlet & DbUtils & JSONIC"でDBデータをJSON出力

スズキです。

縁あって、作ってみました。

--------【Java】--------
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
  Connection connection = null;
  try {
    Class.forName("com.mysql.jdbc.Driver");
    String url =
"jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=UTF8";
    connection = DriverManager.getConnection(url, "root", "");
    QueryRunner runner = new QueryRunner();
    String sql = "SELECT * FROM SUZ_LAB";
    List<Map<String, Object>> list = (List<Map<String, Object>>)runner.query(
      connection, sql, new MapListHandler()
    );
    resp.setContentType("text/javascript");
    resp.setCharacterEncoding("UTF-8"); // 無いと文字化け
    JSON.encode(list, resp.getOutputStream(), true);
  } catch(Exception e) {
    e.printStackTrace();
  } finally {
    DbUtils.closeQuietly(connection);
  }
}
--------

▼ DbUtils
http://commons.apache.org/dbutils/

▼ JSONIC
http://jsonic.sourceforge.jp/

JSONにしてしまえば、後はjQueryで...

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

Androidの開発環境

スズキです。

AndroidはJavaで開発できます。

そして、AndroidのサイトにはEclipseのプラグインがありました。
http://developer.android.com/sdk/eclipse-adt.html

上記サイトの詳しい手順があるので、それにそって行えば、
簡単にインストールできます。(まあ、結局は下記をおさえておけばOKですが...)

▼アップデートサイト
https://dl-ssl.google.com/android/eclipse/
▼インストールパッケージ
Android DDMS
Android DevelopmentTools

でも、これだけでは、だめで、
"Android SDK"をちゃんとインストールしておく必要があります。
http://developer.android.com/sdk/index.html

インストールは適当な場所にアーカイブファイルを展開するだけですが、
その中に、"SDK Setup.exe"あり、それを起動し、
"Android SDK and AVD Manager"を立ち上げ、
パッケージというものを、インストールしていきます。

"Android SDK and AVD Manager"の左側に"Available Pakages"という項目があり、
そこから、インストール可能なパッケージを確認できます。

とりあえず、下記の2.0系を入れてみました。
- Documentation for Android SDK, API 5, revision 1
- Android SDK Tools, revision 3
- SDK Platform Android 2.0, API 5, revision 1
- Google APIs by Google Inc., Android API 5, revision 1
- Usb Driver package, revision 2

利用するには、"Android SDK and AVD Manager"の左側一番上の、
"Virtual Devices"も作っておく必要があります。

ここまで準備できたら、Eclipseに戻って、"Preferences(設定)"から、
Androidの項目を選択し、上記でインストールしてSDKのパスを設定します。

これで、Androidプロジェクトを作成し、実行すると、
エミュレーターが立ち上がり、製作物を確認することができます。

iPhoneもやらないと...

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

"suz-lab - search" 復活!

スズキです。

独自ドメインにして、使い物にならなくなっていた、
右上の検索ボックス(suz-lab - search)ですが、復活させました。

今回の検索対象は、

- www.suz-lab.com(GAE/J)
- blog.suz-lab.com(Blogger)
- http://jbbs.livedoor.jp/computer/41921/(livedoorしたらば掲示板)
- memo.suz-lab.com(Twitter, Tumblr, Delicious)

です。

まあ、自分用です。

そろそろ、画像系、いくか...

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

"mysqlbinlog"使ってみた

スズキです。

"mysqlbinlog"は「バイナリログファイルを処理するためのユーティリティ」です。
http://dev.mysql.com/doc/refman/5.1/ja/mysqlbinlog.html

バイナリログファイルは下記のようにmy.confに記述していると、

--------【my.conf】--------
...
datadir = /usr/local/mysql
log-bin=mysql-bin
...
--------

"mysql-tritonn-bin.XXXXXX"といった形で出力されているはずです。

これを、下記のように実行すると、
# mysqlbinlog mysql-tritonn-bin.XXXXXX
実際に実行されているSQL(人間が読める形)に変換されます。

"MySQL & DRBD & Heartbeat"もそろそろやっておきたいなー。

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

2009年11月6日金曜日

Google Chrome Portable 3.0.195.27 Revision 2 リリース

スズキです。

下記で、"Google Chrome"を"Portable App"のものに切り替えたのですが、
http://blog.suz-lab.com/2009/10/portableappscomgoogle-chrome-portable.html
さらにアップデートしたものが出ています。
http://portableapps.com/news/2009-10-28_-_google_chrome_portable_3.0.195.27_rev_2

"Google Chrome 4"の"Bookmark Sync"はよくわからなかった...

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

"GAE & T2 & POI"で"Google Spredsheet"を読み込み

スズキです。

下記"Google Spredsheet"のテーブル定義から
http://blog.suz-lab.com/2009/11/google-docsspreadsheet.html
DDLを作成したいと思い、せっかくなんでGAE/JのWebアプリでってことで、
実験してみました。

上記のテーブル定義は、下記でExcelのファイルとして取得できるので、
http://spreadsheets.google.com/pub?key=tWC2LhgkL2-xZIb1Wpc9b6w&output=xls
これをPOIで処理できれば、って考えてます。

結果はこんな感じで、無事読み込めました。
http://www.suz-lab.com/sandbox/download

ソースはこんな感じです。
▼JAVA
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/src/suz/lab/apps/page/sandbox/DownloadPage.java
▼JSP
http://code.google.com/p/suz-lab-gae/source/browse/trunk/suz-lab-apps/war/sandbox/download.jsp

ということで、次は、POIでテーブル定義情報を取得して、DDLの作成です。

いつになるかは、未定...

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

2009年11月5日木曜日

コマンドプロンプトでのリネームコマンドは"ren"

スズキです。

使う機会がめったに無かったので...

ということで、"Command Prompt Portable"の
"commandprompt.bat"にも追加です。
http://blog.suz-lab.com/2009/10/command-prompt-portable-bazaar-portable.html

当然、「mv」として!

--------【commandprompt.bat】--------
@echo off
color 07
prompt $p$g
title Command Prompt Portable
set BZR_HOME=S:\windows\sbin\bzr
set PATH=%PATH%;%BZR_HOME%;
doskey cat=type $*
doskey ls=dir $*
doskey rm=del $*
doskey pwd=cd
doskey mv=ren $*
cls
ver
cd \common\home
--------

Windows7でポータブルアプリ動くのかなー...

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

EBSのスナップショットをローテーション

スズキです。

タイトルがわかりづらいかも知れませんが、
常にEBSのスナップショットを、例えば3世代といったかたちで、
ローテーションさせて作成するシェルスクリプトです
ってことです。

まず、スナップショットの作成です。

ec2-create-snapshot \
-K $AWS_PRIVATE_KEY \
-C $AWS_CERTIFICATE \
$EBS_VOLUME

次にスナップショットのIDリストの取得です。
とりあえず、スナップショットリストの取得はこんな感じです。

ec2-describe-snapshots \
-K $AWS_PRIVATE_KEY \
-C $AWS_CERTIFICATE

SNAPSHOT snap-1 vol-0 completed 2009-11-05T07:12:25+0000
SNAPSHOT snap-2 vol-0 completed 2009-11-05T09:20:17+0000
SNAPSHOT snap-3 vol-0 completed 2009-11-05T09:23:41+0000

これを5列目の日付で降順にソートします。

ec2-describe-snapshots \
-K $AWS_PRIVATE_KEY \
-C $AWS_CERTIFICATE \
| sort -k5 -r

SNAPSHOT snap-3 vol-0 completed 2009-11-05T09:23:41+0000
SNAPSHOT snap-2 vol-0 completed 2009-11-05T09:20:17+0000
SNAPSHOT snap-1 vol-0 completed 2009-11-05T07:12:25+0000

※"-k5"で5列目の日付をソート対象にいして"-r"で降順にしています。

さらに、2列目のスナップショットIDのみにしぼります。

ec2-describe-snapshots \
-K $AWS_PRIVATE_KEY \
-C $AWS_CERTIFICATE \
| sort -k5 -r | awk '{print $2}'

snap-1
snap-2
snap-3

※"$2"で2列目のみ表示しています。

そして、上記で作成したスナップショットIDリストに対して、
新しい順で世代分はそのまま残し、それより古いものは削除処理をします。

COUNT=1
for SNAPSHOT in $SNAPSHOTS; do
  if [ $COUNT -le $EBS_GENERATION ]; then
    echo $SNAPSHOT "remained"
  else
    ec2-delete-snapshot \
    -K $AWS_PRIVATE_KEY \
    -C $AWS_CERTIFICATE \
    $SNAPSHOT
    echo $SNAPSHOT " deleted"
  fi
  COUNT=`expr $COUNT + 1`
done

最後に、あらためて全スクリプトです。

--------ここから-------
ec2-create-snapshot \
-K $AWS_PRIVATE_KEY \
-C $AWS_CERTIFICATE \
$EBS_VOLUME

SNAPSHOTS=`ec2-describe-snapshots \
-K $AWS_PRIVATE_KEY \
-C $AWS_CERTIFICATE \
| sort -k5 -r | awk '{print $2}'`

COUNT=1
for SNAPSHOT in $SNAPSHOTS; do
  if [ $COUNT -le $EBS_GENERATION ]; then
    echo $SNAPSHOT "remained"
  else
    ec2-delete-snapshot \
    -K $AWS_PRIVATE_KEY \
    -C $AWS_CERTIFICATE \
    $SNAPSHOT
    echo $SNAPSHOT " deleted"
  fi
  COUNT=`expr $COUNT + 1`
done
--------ここまで--------

AMIに反映しなきゃ。

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

2009年11月4日水曜日

"Google Docs(Spreadsheet)"でテーブル定義

スズキです。

いろいろ試してみたけど、
共同開発では、これが一番いいような気がする。
(みんなでリアルタイムでいじれるから)

ということで、よくある、都道府県、市区町村、郵便番号で作ってみた。
http://spreadsheets.google.com/pub?key=tWC2LhgkL2-xZIb1Wpc9b6w&gid=0

API使ってDDL生成できれば...

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

OpenOffice.org Portable 3.1.1 リリース

スズキです。

"3.1.1"がリリースされています。
http://portableapps.com/news/2009-11-02_-_openoffice.org_portable_3.1.1

近頃、CalcでDB定義書書いていますが、。
ちょっと挙動が不安定だったので、このアップデートでなおったらなー、
と思っています。

アップデートしたアプリ、まだまだあるなー。

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

2009年11月2日月曜日

MySQLでインデックス情報を取得

スズキです。

下記で取得できます。

mysql> show index from SUZ_LAB;

*************************** 1. row ***************************
Table: SUZ_LAB
Non_unique: 1
Key_name: U1
Seq_in_index: 1
Column_name: SUZ_LAB_MAIL
Collation: A
Cardinality: 4426
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE
Comment:

DBドキュメント作成も、もう少しだ...

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

2009年10月27日火曜日

PortableApps.comに"Skype Portable"

スズキです。

SkypeもPortableApps.comからポータブル版がリリースされました。
http://portableapps.com/news/2009-10-22_-_skype_portable_4.1

今までは、以下のサイトで紹介されているやり方で
http://kengo.preston-net.com/archives/002415.shtml
バージョンアップするたびに、非常に面倒だったんですが、
PortableApps.comが適宜バージョンアップしてくれれば、楽チンです。

SkypeとPidginは連携させないほうがいいなー。

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

2X Client Portable - リモートデスクトップ(Windows)のポータブル版

スズキです。

ついに、リモートデスクトップのポータブル版を見つけました!
PortableApps.comからリリースされています。
http://portableapps.com/news/2009-10-20_-_2x_client_portable_7.3.727

パフォーマンスは標準のリモートデスクトップと、ほぼ同じだと思います。

リモートデスクトップをポータブルにできるのは、大きいなー。

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

PortableApps.comに"Google Chrome Portable"

スズキです。

今まで、下記のように"Google Chrome"のポータブル版を使っていましたが、
PortableApps.comからもリリースされました。
http://portableapps.com/news/2009-10-21_-_google_chrome_portable_3.0.195.27

バージョンもこちらのほうが(ほんの)少し高いので、乗り換えました。

早く、"Version 4"、でないかなー?

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

2009年10月21日水曜日

"s3sync"でS3と同期

スズキです。

s3fsがだめそうなので、
http://blog.suz-lab.com/2009/10/s3fsfolder.html
フォルダ(ファイル)同期は"s3sync"で行うことにしました。

導入は下記のとおりです。

▼ 必要パッケージのインストール(rubyが必要)

# yum install ruby

▼ ダウンロードとインストール

# cd /usr/local/src
# curl -O http://s3.amazonaws.com/ServEdge_pub/s3sync/s3sync.tar.gz
# tar xvzf s3sync.tar.gz
# cd /opt
# mv /usr/local/src/s3sync ./
("/opt/s3sync"にインストールしています)

▼ "/opt/s3sync"以下に設定ファイルを作成し、環境変数を設定

# export S3CONF=/opt/s3sync

--------【s3config.yml】
aws_access_key_id: 11111111111111111111111
aws_secret_access_key: 222222222222222222222
--------

で、こんな感じに実行すると、

# ./s3sync.rb -r /var/www/html/img cdn.suz-lab.com:

"cdn.suz-lab.com"以下にimgフォルダが同期されます。

s3fsでいろいろいじったimgフォルダが消せなくなった...
Error - Setting ACL failed - /cdn.suz-lab.com/img_$folder$, Your
request was missing a required header
だって...

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

s3fsでフォルダに"_$folder$"がついてしまう...

スズキです。

下記でs3fsの導入まで完了し、早速、本格的に利用してみようとしたら...
http://blog.suz-lab.com/2009/10/s3fs.html

S3上で作成したフォルダが、s3fsでマウントしたマウントポイントでは、
フィルだ名に"_$folder$"がついた形で、表示されてしまってました...

具体的には、

【S3】
test.txt (ファイル)
img (フォルダ)

【/mnt/s3】
test.txt (ファイル)
img_$folder$ (ファイル)

となってしまいます...

これだと、ちょっと今やろうしていることには利用できないので、
違うソリューション探さないといけません...

ソース修正する元気はないなー...

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

2009年10月20日火曜日

"sed"の復習(外部ファイル)

スズキです。

"mod_ext_filter"で適当なコマンドで文字列置換するので、
そのコマンドにsedを利用しようと思っての復習です。

置換元ファイルはこんな感じです。

-------sample.txt--------
sample1 sample2
sample3 sample4
--------

今回は、置換ルールは外部ファイルということで、
こんな、簡単なものを用意しました。

--------sample1.sed--------
s/s/S/
--------

で、実行です。

# sed -f sample1.sed < sample.txt
Sample1 sample2
Sample3 sample4

行の最初にマッチングしたものしか置換されません。
全部置換するには、下記のように"g"をつけます。

--------sample2.sed--------
s/s/S/g
--------

で、また実行です。

# sed -f sample2.sed < sample.txt
Sample1 Sample2
Sample3 Sample4

置換ルールはこんな感じに複数かけます。

--------sample3.sed--------
s/s/S/g
s/a/A/g
--------

で、これも実行です。

# sed -f sample3.sed < sample.txt
SAmple1 SAmple2
SAmple3 SAmple4

よし、思い出してきたぞ!

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

"hosts"ファイルの切り替えをFirefoxのアドオンで

スズキです。

こちらで、SwitchHostsという便利なFirefoxのアドオンが紹介されてました。
http://phpspot.org/blog/archives/2009/10/hostsfirefoxswi.html

"hosts"ファイルの切り替え/編集をFirefox上で行うことができます。

ダウンロードはこちらから。
https://addons.mozilla.org/ja/firefox/addon/14258

ローカルVMware上のApacheのVirtualHostの名前解決用に、
内部DNS立ち上げようと思ってたけど、これでいいや...

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

2009年10月19日月曜日

s3fs

スズキです。

下記準備から、
http://blog.suz-lab.com/2009/10/cloudfront.html

今度は、実際にs3fsを利用してみました。

まずは、必要なパッケージの導入です。(環境によって異なるはずです)

# yum install make
# yum install gcc-c++
# yum install fuse-devel
# yum install curl-devel
# yum install libxml2-devel

次に、s3fsのインストールです。

# curl -O http://s3fs.googlecode.com/files/s3fs-r177-source.tar.gz
# tar xvzf s3fs-r177-source.tar.gz
# cd s3fs
# make
# make install

最後に、s3fsの実行です。
# s3fs cloudfront.suz-lab.com -o accessKeyId=aaa -o
secretAccessKey=sss /mnt/cloudfront

予め作成しておいた、s3のbucket(cloudfront.suz-lab.com)を
"/mnt/cloudfront"にマウントしています。

次は「mod_ext_filter」だ。

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

CloudFrontを効率的に利用する(情報のまとめ)

スズキです。

EC2(CentOS 5.3)上のWebアプリケーションで
静的なファイル(js, css, jpg, png, gif, swf, ...)に対してCloudFrontを利用することになり、
下記内容の技術を利用すれば、効率的に運用できるんじゃないか?
と企んでます。

▼mod_ext_filter(URL書き換え)
http://vkgtaro.jp/2009/04/05/mod_ext_filter

▼s3fsの本家
http://code.google.com/p/s3fs/

▼s3fsの導入方法
http://d.hatena.ne.jp/rx7/20080429/p1

本来なら、CloudFrontを利用するファイルをS3にアップロードし、
HTML内のURLをCloudFrontのものに書き換えるって話になると思いますが、
CloudFrontを利用しない開発(テスト)環境と透過的にしたいと思ってます。

早速、VMwareで実験しよう。

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

"define"でなく"const" - PHP

スズキです。

うれしいことに、この記事に対して、
http://blog.suz-lab.com/2009/10/php.html

このようなコメントをいただきました。
http://www.haloscan.com/comments/iretsuzuki/7658573933420455263/?a=22777
> MobileTool.php のソースを拝見させていただきました。
> 定数定義されている
> define("MOBILE_CAREER_DOCOMO" , 1);
> 等を以下のようにクラス定数にすれば、グローバル空間を汚染せず、
> より隠蔽化されたライブラリになると思います。
> class MobileTool extends Tool {
>   const MOBILE_CAREER_DOCOMO = 1;
>   :
>   :
>   public function isCareerDocomo() {
>     return ($this->career === self::MOBILE_CAREER_DOCOMO);

ということで、早速反映してコミットです。
http://code.google.com/p/suz-lab-php/source/browse/trunk/suz-lab-php/php/lib/Tool/MobileTool.php

HaloScanはやめたいなー...

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

2009年10月15日木曜日

Tomcat6でログ出力を調整(server.xml)

スズキです。

設定をserver.xmlで行っているTomcat6の特定のコンテキストの
ログ出力を変更する必要があり、実際やってみたら、結構はまってしまいました…

まず、アクセスログに関してですが、
これは、下記のようにserver.xmlを設定することですぐに実現できました。
(まあ、有名なお話です…)

--------【server.xml】--------
...
<Context path="/suz-lab" docBase="/home/suz-lab/webapp">
  <Valve className="org.apache.catalina.valves.AccessLogValve"
    directory="/home/suz-lab/log"
    prefix="suz-lab_access-"
    fileDateFormat="yyyy-MM-dd"
    suffix=".log"/>
</Context>
...
--------

で、はまったのがエラーログ(catalina.outに出るやつ)です。

まずは、素直に、下記のようにserver.xmlを指定したのですが、
出力されず...

--------【server.xml】--------
...
<Context path="/suz-lab" docBase="/home/suz-lab/webapp">
  <Logger className="org.apache.catalina.logger.FileLogger">
    directory="/home/suz-lab/log"
    prefix="suz-lab_error-"
    suffix=".log"
    timestamp="true">
  </Logger>
</Context>
...
--------

んー、"Logger"は"Context"の中に記述してもいいはず何だけどな...
と思いながら、いろいろ調べていると、こんな情報が...

> Tomcat 6.0 での重要な変更点として,localhost_log を作るために
> 前のバージョンにあった <Logger> エレメントは,もはや <Context> の
> 妥当な入れ子エレメントではない。そのかわりに,デフォルトの
> Tomcat コンフィグレーションは java.util.logging を使う。
http://www.oki.com/jp/oss/document/tomcat/tomcat60-docs-ja/logging.html

ということで、「logging.properties」のほうで設定しないといけない、
ということでした...

で、見よう見まねで、下記のように設定すると、無事、指定した場所にログが出力されました。
("handlers"に加えるのを忘れないように!)

--------【logging.properties】--------
handlers = 1catalina.org.apache.juli.FileHandler,
2localhost.org.apache.juli.FileHandler,
3manager.org.apache.juli.FileHandler,
4admin.org.apache.juli.FileHandler,
5host-manager.org.apache.juli.FileHandler,
6suzlab.org.apache.juli.FileHandler,  java.util.logging.ConsoleHandler

...

6suzlab.org.apache.juli.FileHandler.level = FINE
6suzlab.org.apache.juli.FileHandler.directory = /home/suz-lab/log
6sualab.org.apache.juli.FileHandler.prefix = suz-lab_error-

...

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/suz-lab].level
= INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/suz-lab].handlers
= 6suzlab.org.apache.juli.FileHandler
--------

久しぶりのTomcatだったなー...

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

Googleドキュメントでフォルダの共有

スズキです。

今日、Googleドキュメントの右上の「New! フォルダの共有」に気づいてしまいました。
なんか、共有フォルダが作れるようです。

実際、左側のマイフォルダ内のフォルダを、右クリックなどで共有フォルダにすることができ、
そのフォルダ内のドキュメントは、すべて共有されるようになっています。

今まで、ドキュメントごとに、いちいち共有設定していましたが、
共有フォルダを利用することで、その煩わしさからも開放されます。

小さめのプロジェクトは、まずは、
Dropboxの共有、Googleドキュメントの共有、Skypeの会議室、
だなー…(セキュリティの話はおいておいて…)

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

FlashDevelop 3.0.5 (日本語化) & Flex 3 SDK 3.4.1

スズキです。

そろそろ、Flashまわりにキャッチアップしないとなー、と思い、
まずは開発環境まわりから、アップデートしてみました。

ということで、まずは"FlashDevelop"。
3.0.5になっていました。
http://www.flashdevelop.org/community/viewtopic.php?f=11&t=5567

ちょうど昨日、日本語化ファイルも、こちらで提供されました。
http://www.logicalyze.net/blog/?p=3151

そして"Flex 3 SDK"。こちらは、3.4.1になっています。
http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+3

とりあえず、環境は整ったので、
リハビリがてらに、"Google Maps API for Flash"でもやってみるか!?

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

2009年10月14日水曜日

PHPで携帯用便利クラス

スズキです。

下記情報をもとに、PHPの携帯用便利クラスを作ってみました。(車輪の再発明...)
(ID取得) http://memorva.jp/memo/mobile/uid_utn.php
(機種判定) http://mobile.np-os.net/index.php?p=ua

ソースはこちらになります。
http://code.google.com/p/suz-lab-php/source/browse/trunk/suz-lab-php/php/lib/Tool/MobileTool.php

使い方はこんな感じです。

--------【PHP】-------

require_once("MobileTool.php");

$mobileTool = new MobileTool(
  $_SERVER["HTTP_USER_AGENT"],
  apache_request_headers()
);

// IDの取得
$mobileTool->id;

// 画面の幅と高さの取得
$mobileTool->screenWidth;
$mobileTool->screenHeight;

// DoCoMo?
$mobileTool->isCareerDocomo()

// FOMA?
$mobileTool->isTypeFoma();

...

--------

実は、自分メモ...

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

EC2 & CentOS(5.3) & OpenVPN 準備編

スズキです。

とりあえず、インターネット上の使えそうな情報をまとめてみました。

▼ Amazon EC2でVPN網構築 (目次)
http://blog.livedoor.jp/dynakou/archives/51373183.html
▼ Amazon EC2でVPN網構築(1)
http://blog.livedoor.jp/dynakou/archives/51321805.html
▼ Amazon EC2でVPN網構築(2) OpenVpnServerインストール
http://blog.livedoor.jp/dynakou/archives/51338453.html
▼ Amazon EC2でVPN網構築(3) OpenVpnServer設定~その1
http://blog.livedoor.jp/dynakou/archives/51338453.html
▼ Amazon EC2でVPN網構築(4) OpenVpnServer設定~その2
http://blog.livedoor.jp/dynakou/archives/51347708.html
▼ Amazon EC2でVPN網構築(5) OpenVpnクライアントの設定
http://blog.livedoor.jp/dynakou/archives/51351466.html
▼ Amazon EC2でVPN網構築(Last) OpenVpnクライアントの設定 CentOS版
http://blog.livedoor.jp/dynakou/archives/51372925.html

▼ CentOS 5.2 に OpenVPN を突っ込むテスト[1]
http://blog.y17e.com/?p=881
▼ CentOS 5.2 に OpenVPN を突っ込むテスト[2]
http://blog.y17e.com/?p=883
▼ CentOS 5.2 に OpenVPN を突っ込むテスト[3]
http://blog.y17e.com/?p=893
▼ CentOS 5.2 に OpenVPN を突っ込むテスト[4]
http://blog.y17e.com/?p=896
▼ CentOS 5.2 に OpenVPN を突っ込むテスト[5]
http://blog.y17e.com/?p=898
▼ CentOS 5.2 に OpenVPN を突っ込むテスト[6]
http://blog.y17e.com/?p=904
▼ CentOS 5.2 に OpenVPN を突っ込むテスト[7]
http://blog.y17e.com/?p=914
▼ CentOS 5.2 に OpenVPN を突っ込むテスト[8]
http://blog.y17e.com/?p=925

まずは上記を精読して、VMware上のCentOSで実験かな。

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