2011年8月9日火曜日

Androidアプリを登録しました

昨日、個人で使うために作ったアプリを公開しました。

どんなアプリかというと、最近使用したアプリのショートカットを表示するウィジェットです。

Android端末のユーザの大半は、その機能はHomeボタン長押しで使えるとご認識しているでしょう。
しかしながら、実は、私の使用しているAndroid端末(IS05)では、Home長押しは「起動中のアプリ」を表示します。
さらには、「最近起動したアプリ」を表示する機能が見当たりません(案外、調べればありそうですが・・・)。

と、いうような経緯があり、それならばウィジェットでその機能を提供し、ワンタッチで起動できるアプリとして作成してみました。

つまるところ、ただの俺得アプリに過ぎませんが、興味がありましたら、使ってみてください。
Android Market - 最近アプリ

2011年7月23日土曜日

JavaSE7のtryは便利

7月28日に正式リリースされるJavaSE7では、いくつか便利な構文が増えます。
今回は、その中でも特に有用であろうtryの新しい構文について調べてみました。

ファイルのコピーを例にして、tryの構文の拡張を調べてみます
JavaSE6以前のソースコードをJavaSE7のコードに置き換えます。

これまで(JavaSE6以前)のコード
FileInputStream fis = null;
FileOutputStream fos = null;
try {
 fis = new FileInputStream("D:\\test.txt");
 fos = new FileOutputStream("D:\\test2.txt");
 byte[] bytes = new byte[1024];
 int size = -1;
 while ((size = fis.read(bytes)) != -1) {
  fos.write(bytes, 0, size);
 }
} catch (FileNotFoundException e) {
 e.printStackTrace();
} catch (IOException e) { 
 e.printStackTrace();
} finally {
 if (fos != null) {
  try {
   fos.close();
  } catch (IOException ex) {
  }
 }
 if (fis != null) {
  try {
   fis.close();
  } catch (IOException ex) {
  }
 }
}

これから、上記のソースコードをJavaSE7風に書き換えていきます。

例外のマルチキャッチ
catchの部分をひとつにまとめることができます
FileInputStream fis = null;
FileOutputStream fos = null;
try {
 fis = new FileInputStream("D:\\test.txt");
 fos = new FileOutputStream("D:\\test2.txt");
 byte[] bytes = new byte[1024];
 int size = -1;
 while ((size = fis.read(bytes)) != -1) {
  fos.write(bytes, 0, size);
 }
} catch (FileNotFoundException | IOException e) {
 e.printStackTrace();
} finally {
 if (fos != null) {
  try {
   fos.close();
  } catch (IOException ex) {
  }
 }
 if (fis != null) {
  try {
   fis.close();
  } catch (IOException ex) {
  }
 }
}
これで、例外処理時に同じコードを書かずに済みます。
また、「|」で複数の例外を指定した場合は、左の例外から評価されるようです。
これまでの構文のcatchで上に書いていた例外は、その順で左から書けばよいでしょう。つまり、Throwableを書く場合は、一番右です。
AutoCloseableインターフェース
JavaSE7から、AutoCloseableというインターフェースが追加されます。
このインターフェースには、closeというメソッドがあります(ご存知のとおり、JavaSE6以前にもcloseメソッドはありますが、それをオーバーライドしている感じです)。このメソッドが「try-finallyの処理で勝手に呼ばれる」ようです。 AutoCloseableを利用するためのコードは下記の通りです。
try (FileInputStream fis = new FileInputStream("D:\\test.txt"); FileOutputStream fos = new FileOutputStream("D:\\test2.txt")) {
 byte[] bytes = new byte[1024];
 int size = -1;
 while ((size = fis.read(bytes)) != -1) {
  fos.write(bytes, 0, size);
 }
} catch (FileNotFoundException | IOException e) {
 e.printStackTrace();
}
tryの()の中ではセミコロンで区切ると、複数のAutoCloseableを並べて記述できるようです。
Stream関係のインターフェースはAutoCloseableのサブインターフェースになっているようです。
インターフェースを実装する際に、AutoCloseableを意識する必要はあまりないと思われます。ъ(゚Д゚)グッジョブ!!(JavaSE6以前のソースを書き換える場合は別)
※public void close() throws IOException => public void close() throws Exception というふうに変わります。

なにはともあれ、コード量がかなり減りました。ワーイヽ(゚∀゚)メ(゚∀゚)メ(゚∀゚)ノワーイ
AutoCloseableを検証
実際に自分の眼で見て確認しました。
closeの順番と、実際に例外が起こった場合について。
public class Test {
 public static void main(String[] args) {
  new Test().start();
 }

 void start() {
  try (AutoCloseableImpl1 ac1 = new AutoCloseableImpl1(); AutoCloseableImpl2 ac2 = new AutoCloseableImpl2()) {
   
  }
 }
}

class AutoCloseableImpl1 implements AutoCloseable {

 AutoCloseableImpl1() {
  System.out.println(getClass().getSimpleName());
 }

 @Override
 public void close() {
  System.out.println(getClass().getSimpleName() + "#close()");
 }
}

class AutoCloseableImpl2 implements AutoCloseable {

 AutoCloseableImpl2() {
  System.out.println(getClass().getSimpleName());
 }

 @Override
 public void close() {
  System.out.println(getClass().getSimpleName() + "#close()");
 }
}
上記のプログラムを実行します。
実行結果は

AutoCloseableImpl1
AutoCloseableImpl2
AutoCloseableImpl2#close()
AutoCloseableImpl1#close()

closeが呼ばれており、closeする順番もオッケー。
ちなみに、AutoCloseableImpl2()コンストラクタで例外を投げた場合は、以下

AutoCloseableImpl1
AutoCloseableImpl1#close()
Exception in thread "main" java.lang.NullPointerException
・・・

期待通りです。

余談ですが、catchもfinallyもないtry構文が書けるようになりました。(内部的にはfinallyが存在しますが)

実は、JavaSE7にはjava.nio.file.Filesというクラスが追加されており、ファイルをコピーするだけなら、このクラスを使ったほうが良いです。
まぁ、今回はあくまで勉強のためですから・・・

今回の検証でもちいたIDEはNetBeansです。
Eclipse3.7でも検証は可能でしたが、不都合な点がありましたので、NetBeansを使用しました。
現在のEclipse3.7とJDK1.7の詳細については こちら

2011年7月17日日曜日

Eclipse3.7 で JavaSE7

Eclipse3.7 で JavaSE7

JavaSE7の正式リリースは2011年7月28日で、NetBeansは既にJavaSE7に対応しています。
EclipseはまだJavaSE7に対応していないのですが、pluginをインストールすれば使えるようになります。

各ツールのサイト
Java™ Platform, Standard Edition 7 Developer Preview Release
Eclipse Classic 3.7
JDT/Eclipse Java 7 Support(BETA)
JDK準備
JDK7 Preview Releaseをダウンロードして、インストールします。
開発ツールだけインストールすればOKです。
Eclipse準備
Eclipseは3.7の「Classic」を使います。
「IDE for Java Developers」や、「IDE for Java EE Developers」では、pluginのインストールができませんでした。
Install New SoftWare -> Work with:
「http://build.eclipse.org/eclipse/java7patch/」を入力して、pluginをインストールします。
Eclipse設定
Preferences -> Java -> Installed JREs
インストールしたJDK1.7を追加し、使用するようにします。
あとは、Java Compilerの設定で、Compiler compliance level を 1.7 にすれば準備完了です。

実は、上記で設定した開発環境には問題があり、メソッドや変数の候補を出せなくなります。
普段から、何も考えずにとりあえず Ctrl + Space 押して幸せになっている私にとっては致命的です。
Eclipseで正式にサポートされるまではJavaSE7は我慢するか、NetBeansを使いましょう。

2011年5月31日火曜日

アルゴリズムを考える

アルゴリズムというものについて考えてみる

Twitterでこんなツイートを見つけました。

自然数nに対して操作F「nが偶数ならばnを2で割る。nが奇数ならばnを3倍して1を加える。」を10回行う。10回目で初めて1となる自然数を全て求めよ。(07京都高校生数学コンテスト)

プログラマとしては、さっとプログラムを書いて結果を出してみたくなるのではないでしょうか。

この解を出すアルゴリズムは大きく分けて2通りあります。
その1.1から1024(2^10)までの全ての数についてチェックして探す
その2.結果の「1」から逆に遡って探す

上記その1のアルゴリズムは2重ループによる処理で、その2は再帰による処理になります。
おそらく、この問題で求められている解き方は2の方法でしょう。

書いてみた
import java.util.ArrayList;
import java.util.Collection;


public class Main {

 /*
  * こいつを呼ぶ
  */
 public static void main(String[] x_args) {
  new Main().start();
 }

 public void start() {
  exec(new F10To1ImplVer1());
  System.out.println();
  exec(new F10To1ImplVer2());
 }

 void exec(F10To1 x_f) {
  {
   System.out.println(x_f.getClass().getSimpleName() + ":start.");
   long time_s = System.currentTimeMillis();
   int count = 10;
   int end = (int) Math.pow(2, count);
   Collection<Integer> values = x_f.getF10To1(end, count);
   for (Integer value : values) {
    System.out.print(value + "\t");
   }
   long time_e = System.currentTimeMillis();
   System.out.println();
   System.out.println(":end.");
   System.out.println("time => " + (time_e - time_s) + "ms");
  }
 }
 
}

interface F10To1 {

 /*
  * 戻り値Collection内の要素の順番は保証しない
  */
 Collection<Integer> getF10To1(int x_end, int x_count);
}

/**
 * 全ての自然数についてチェックするパターン
 */
class F10To1ImplVer1 implements F10To1 {

 public Collection<Integer> getF10To1(int x_end, int x_count) {
  Collection<Integer> result = new ArrayList<Integer>();
  // 1...最後までのループ
  for (int i = 1 ; i <= x_end ; i++) {
   if (isF10To1(i, x_count)) {
    result.add(Integer.valueOf(i));
   }
  }
  return result;
 }

 private boolean isF10To1(int x_value, int x_count) {
  int tmp = x_value;
  // 9回処理する
  for (int i = 0 ; i < x_count - 1 ; i++) {
   tmp = f(tmp);
   if (tmp == 1) {
    // 1になったら対象外
    return false;
   }
  }
  // 10回目
  tmp = f(tmp);
  return tmp == 1;
 }

 /*
  * 1回分の処理
  */
 private int f(int x_value) {
  if (x_value % 2 == 0) {
   return x_value / 2;
  }
  return x_value * 3 + 1;
 }
}

/**
 * 逆から辿るパターン
 */
class F10To1ImplVer2 implements F10To1 {

 public Collection<Integer> getF10To1(int x_end, int x_count) {
  int tmp = 1;
  // 再帰処理でチェックします
  Collection<Integer> result = new ArrayList<Integer>();
  revF(result, tmp, x_count);
  return result;
 }

 private void revF(Collection<Integer> x_values, int x_value, int count) {
  boolean can3 = canRevF_3(x_value);
  int v2 = revF_2(x_value);
  int v3 = revF_3(x_value);
  count--;
  if (count < 1) {
   if (can3) {
    x_values.add(Integer.valueOf(v3));
   }
   x_values.add(Integer.valueOf(v2));
   return;
  }
  if (can3 && (1 < v3)) {
   revF(x_values, v3, count);
  }
  if (v2 != 1) {
   revF(x_values, v2, count);
  }
 }

 /*
  * 引数が2で割った結果だとした場合の元の数を返す
  */
 private int revF_2(int x_value) {
  return x_value * 2;
 }

 /*
  * 引数が3倍して1を足した結果だとした場合に元の数が存在するか判断する
  */
 private boolean canRevF_3(int x_value) {
  if (x_value < 4) {
   return false;
  }
  if ((x_value - 1) % 3 != 0) {
   return false;
  }
  return (x_value - 1) / 3 % 2 == 1;
 }

 /*
  * 引数が3倍して1を足した結果だとした場合の元の数を返す
  */
 private int revF_3(int x_value) {
  return (x_value - 1) / 3;
 }
}
もしかしたら上記の実装に間違いがあるかもしれません。その場合は、ご指摘いただけるとありがたいです。
F10To1ImplVer1が総当り(その1)で、F10To1ImplVer2が再帰処理を行う(その2)クラスです。
10回程度のループではそれほど差が出ません(マシンにもよります)が、20回程に回数を増やすと極端に処理時間に差が出ます。

上記の2通りのアルゴリズムで、私が先に書いたのは総当り(その1)の方です。
その後、少し考えてその2の方法を書きました。

普段から簡単にプログラムを書こうとしているせいか、「本来はどういうアルゴリズムであるべきか」をあまり考えなくなってしまったようです。

私は細かい実装はあまり気にしないのですが、気にせずともより良いアルゴリズムで書けるのが理想的ですね。

2011年4月24日日曜日

パッケージ内のクラス一覧を取得する

普段からSpringなどのDIフレームワークを使っているのですが、 Springでは、xmlファイルでパッケージ名を指定すると、そのパッケージ内のクラスをインスタンス化して注入してくれます。
便利な機能なのでよく使うのですが、どのようにしてパッケージ名からクラス一覧を取得しているかを知りませんでした。

実装を知るには、ソースを読めば手っ取り早いですが、今回はソースは読まずに、実装方法はこんな感じだろうかと考えてみます。

パッケージ名から、そのパッケージ内のクラス一覧を取得する
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;

/**
 * 指定パッケージ内のクラス情報のコレクションを返す。
 * 
 * @param x_packageName パッケージ名
 * @return クラス情報のコレクション
 */
public static Collection<Class<? extends Object>> getClasses(String x_packageName) {
 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 JavaFileManager fm = compiler.getStandardFileManager(new DiagnosticCollector<JavaFileObject>(), null, null);

 // 一覧に含めるオブジェクト種別。(クラスのみを含める)
 Set<JavaFileObject.Kind> kind = new HashSet<JavaFileObject.Kind>();
 kind.add(JavaFileObject.Kind.CLASS);

 Collection<Class<? extends Object>> results = new ArrayList<Class<? extends Object>>();

 Iterable<JavaFileObject> jfos = null;
 try {
  jfos = fm.list(StandardLocation.CLASS_PATH, x_packageName, kind, false);
 } catch (IOException e) {
  e.printStackTrace();
 }
 if (jfos != null) {
  for (JavaFileObject fos : jfos) {
   try {
    Class<? extends Object> clazz = Class.forName(x_packageName + "." + fos.getName().replaceAll("\\.class$", ""));
    if (clazz.isEnum() || clazz.isInterface() || clazz.isAnonymousClass() || clazz.isLocalClass()) {
     continue;
    }
    results.add(clazz);
   } catch (ClassNotFoundException e) {
    e.printStackTrace();
   }
  }
 }
 return results;
}
使用するのはjava.lang.reflectパッケージのクラスかと思っていましたが、実際はjavax.toolsパッケージのクラスです。
これらのクラスがあるのは、javaのバージョン1.6以降のようです。
バージョン1.5以前ではもうちょっと面倒なやり方になると思います。

フレームワークの実装を知ることで、フレームワークの使い方を知ります。
今回は、実装を想像しただけですが...

2011年4月18日月曜日

ウェイトアップしています

現在、私は週2回ほど筋トレをしています。 始めたのは2010年8月頃で、半年以上は継続していることになります。
体の変化については、体重62kgから68kgになり、体脂肪は14%から15.5%になりました。 今のところはいい感じにウェイトアップしています。 ちなにみ、身長は180cmなので、まだまだ痩せ体型といえます。

今回は、筋トレについて。 まだまだ素人ですが、これまでの私の経験に基づいて、ウェイトアップの方法を説明します。
この説明が参考になる人は、おそらく私のような太りにくい痩せ型の体型でしょう。 該当する人も、そうでない人も、一例として見てもらいたいと思います。

まず、ウェイトアップで最も重要なのは、トレーニングよりも栄養の摂取です。 なので、今回は私が日頃、どのような栄養(サプリメント)を摂取しているかと、それはどのタイミングなのかについて説明します。

プロテイン
種類原材料特徴
ホエイ牛乳吸収が速い
カゼイン牛乳吸収が遅い
エッグ良質のタンパク質短時間で消化
大豆大豆健康に良く消化が早い
ザ・バルクアップトレーニングより
とりあえず、ホエイと大豆(ソイ)だけ注目しておけばよいです。 摂取する種類と時間については、運動前後はホエイ、就寝前は大豆(ソイ)が妥当でしょう。
炭水化物
特に痩せ体質の人が筋肉量アップをするには炭水化物がタンパク質以上に重要な役割を果たすと考えています。
ザ・バルクアップトレーニングより
サイトの説明通りで、プロテインより重要です。 炭水化物の摂取量が少ないと、体重は増えません。 私は以前に炭水化物を制限してトレーニングしていた事があるのですが、そのときは体重は全く増えず、体脂肪だけが落ちていきました。 それはそれでありかと思われるかもしれませんが、トレーニングしている事と健康面から考えて、十分に摂取すべきです。 少し多めに摂取して、超過した分はランニングすることで落とすくらいがちょうどいいでしょう。
BCAA
トレーニング時の疲労の軽減にも効果がありますのでハードなトレーニングでも疲労が軽減することで運動パフォーマンスを最大限に生かす事ができ、よりトレーニング効果を高めてくれます。
ザ・バルクアップトレーニングより
サイトの説明通りで、効率よくトレーニングするために必要になります。 筋トレの目的は、筋肉に可能な限り負荷をかけることです。 トレーニングをしていて、体力が無くなるより先に筋肉に十分な負荷をかけられればいいのですが、 プロテイン等を摂取していると、それよりも先に体力が無くなる事がありえます。 そうなると、効率よく負荷をかけ続けることが難しくなるので、疲労を軽減するBCAAをトレーニング前に摂取しておきます。
グルタミン
グルタミンは筋肉の分解を抑えてくれるのでウエイトとレーニン後には必ず摂っておきたいサプリメントです。
グルタミンは体内のアミノ酸の60%を占めていることからも異化を抑えてくれるサプリメントの中でも一番効果的です。
ザ・バルクアップトレーニングより
筋肉を増加させるための筋トレですが、その筋トレ中(主に筋トレ後)にも筋肉は異化(分解みたいなもの)します。 そもそも、筋肉が異化するのは体内のタンパク質(アミノ酸)が不足しているためなので、異化する筋肉の代替として摂取します。 即効性のプロテインのようなものと考えればいいかと思います。
私が購入しているサプリメント
グルタミン DNS グルタミンパウダー(300g)
BCAA DNS BCAAパウダー(200g)
ホエイプロテイン ザバス(SAVAS) ホエイプロテイン100 ココア味 360g
ホエイプロテイン(ウェイトアップ) ザバス(SAVAS) ウェイトアップ 360g
大豆(ソイ)プロテイン ザバス(SAVAS) ウエイトダウン 360g
ホエイプロテイン(ウェイトアップ)はタンパク質の量が少なく、炭水化物が含まれています。
全部一括で買い揃えると相当の金額になります。
いったん揃えてしまえば、あとは無くなったものを補充していくだけなので、金額的にはそれほど気にならないと思います。
上記のプロテインは飲みやすさで選んでいますが、探せば他にも安いものや、高価なものがあります。
実際にはどのように摂取しているか
週7日のうち、2日がトレーニング日で、時間帯は18:00~21:00の間の90分です。その前後と就寝前に摂取しています。
また、運動後はすぐに炭水化物を摂取するようにします。無理矢理でも詰め込みます。
トレーニング前トレーニング後就寝前
トレーニング日ホエイプロテイン
BCAA
ホエイプロテイン(ウェイトアップ)
グルタミン
トレーニングの無い日大豆プロテイン
グルタミン
注意してほしい点が一つ
サプリメントを摂取するということについて、特にホエイプロテインは動物性タンパク質なので、 どうしても胃や腸に大きな負担をかけてしまいます。
サプリメントの過剰な摂取は避けて、日頃の食事で野菜やヨーグルト、ビタミン剤を摂るようにしたほうがよいでしょう。
ちなみに、私は、スキムミルクで作る自家製ヨーグルトを毎朝食べて腸の調子を整えています。

2011年4月8日金曜日

Struts2 + Spring + Slim3 サンプル

今回は、 【修正版】 Struts2 + Spring + Slim3で構築したフレームワークを使ってサンプルのソースを書いてみます。

サンプルは簡単なデータ登録と表示の実装です。

作成するファイル一覧
今回、作成するファイルは以下の通りです。それ以外のファイルは一切触りません。
ファイル一覧
/src/com/brightgenerous/s3/sample/action/Sample_Action.java
/src/com/brightgenerous/s3/sample/action/Sample_Service.java
/src/com/brightgenerous/s3/sample/action/Sample_ServiceImpl.java
/src/com/brightgenerous/s3/sample/data/model/Account.java
/src/com/brightgenerous/s3/sample/data/meta/AccountMeta.java
/war/WEB-INF/content/sample/index.jsp
/war/WEB-INF/content/sample/show.jsp
「Account.java」と「AccountMeta.java」はSlim3のプラグインを使って自動生成します。
javaのソースコード
1.com.brightgenerous.s3.sample.action.Sample_Action.java
package com.brightgenerous.s3.sample.action;

import java.util.List;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.springframework.beans.factory.annotation.Autowired;

import com.brightgenerous.s3.sample.data.model.Account;
import com.opensymphony.xwork2.ModelDriven;

@ParentPackage("struts-default")
@Namespace("/sample")
public class Sample_Action implements ModelDriven {

 private Account p_model = new Account();

 @Autowired
 private Sample_Service p_service;

 @Override
 public Account getModel() {
  return p_model;
 }

 public List getAccounts() {
  return p_service.get();
 }

 /**
  * [Action:初期表示]
  */
 @Action("index")
 public String index() {

  return "index";
 }

 /**
  * [Action:表示]
  */
 @Action("show")
 public String show() {

  return "show";
 }

 /**
  * [Action:登録]
  */
 @Action("regist")
 public String regist() {
  p_service.regist(p_model);
  return show();
 }
}
2.com.brightgenerous.s3.sample.action.Sample_Service.java
package com.brightgenerous.s3.sample.action;

import java.util.List;

import com.brightgenerous.s3.sample.data.model.Account;

interface Sample_Service {

 Account regist(Account x_account);

 List<Account> get();
}
3.com.brightgenerous.s3.sample.action.Sample_ServiceImpl.java
package com.brightgenerous.s3.sample.action;

import java.util.List;

import org.slim3.datastore.Datastore;
import org.springframework.stereotype.Component;

import com.brightgenerous.s3.sample.data.meta.AccountMeta;
import com.brightgenerous.s3.sample.data.model.Account;

@Component
class Sample_ServiceImpl implements Sample_Service {

 @Slim3GlobalTransaction
 private GlobalTransaction p_tx;

 @Override
 public Account regist(Account x_account) {
  p_tx.put(x_account);
  return x_account;
 }

 @Override
 public List get() {
  AccountMeta meta = AccountMeta.get();
  return Datastore.query(meta).asList();
 }
}
4.com.brightgenerous.s3.sample.data.model.Account.java
package com.brightgenerous.s3.sample.data.model;

import java.io.Serializable;

import com.google.appengine.api.datastore.Key;

import org.slim3.datastore.Attribute;
import org.slim3.datastore.Model;

@Model(schemaVersion = 1)
public class Account implements Serializable {

 private static final long serialVersionUID = 1L;

 @Attribute(primaryKey = true)
 private Key key;

 @Attribute(version = true)
 private Long version;

 private String nickname;

 private String email;

 /**
  * Returns the key.
  * 
  * @return the key
  */
 public Key getKey() {
  return key;
 }

 /**
  * Sets the key.
  * 
  * @param key the key
  */
 public void setKey(Key key) {
  this.key = key;
 }

 /**
  * Returns the version.
  * 
  * @return the version
  */
 public Long getVersion() {
  return version;
 }

 /**
  * Sets the version.
  * 
  * @param version the version
  */
 public void setVersion(Long version) {
  this.version = version;
 }

 public String getNickname() {
  return nickname;
 }

 public void setNickname(String x_nickname) {
  nickname = x_nickname;
 }

 public String getEmail() {
  return email;
 }

 public void setEmail(String x_email) {
  email = x_email;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((key == null) ? 0 : key.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj) {
   return true;
  }
  if (obj == null) {
   return false;
  }
  if (getClass() != obj.getClass()) {
   return false;
  }
  Account other = (Account) obj;
  if (key == null) {
   if (other.key != null) {
    return false;
   }
  } else if (!key.equals(other.key)) {
   return false;
  }
  return true;
 }
}
Slim3プラグインのantから「gen-model」を選択して、自動生成されたソースをもとにしています。
追加部分は「nickname」と「email」の変数と、そのgetter・setterだけです。
jspのソースコード
1./WEB-INF/content/sample/index.jsp
<%@page pageEncoding="UTF-8" isELIgnored="false" session="false"%>
<%@ taglib uri="/struts-tags" prefix="s" %>


 
 
 

 

 表示
2./WEB-INF/content/sample/show.jsp
<%@page pageEncoding="UTF-8" isELIgnored="false" session="false"%>
<%@ taglib uri="/struts-tags" prefix="s" %>


  
ニックネームEmail
戻る

以上で実装は完了です。

ここまで書いておいて、言ってしまうのですが、GlobalTransactionのインスタンスが直に注入されるのは気持ち悪いです。無理にSpringを使うことは無いと思い直しました。さらに、Springだけが原因かは調べていませんが、spin-upの時間もかなり掛かっています。

実行してみる
折角なので、動かしてみます。

Transactionは上手く機能しました。画像ではお見せできていませんが、例外が発生した場合は、ちゃんとrollbackしてくれています。

なんとかNo-configurationで書けたのですが、この方法を続けるにはDIを軽量化する必要があるのかなぁ...と思ってみたりみなかったり。

近いうちに、GWTとStruts2の連携についても書ければいいかな、なんて考えています。

2011年3月31日木曜日

enumを使いたおしたい

こういうパターンはいかがでしょうか?という内容です。

javaのenumについてです。

先日、ふと、Effective Javaを読み直してenumについての認識を改めました。

enumの変数というのは、「唯一のインスタンス」
その結果、定数のようにイコールの演算子で一致するのです。

以前からそれは理解していました。
が、実は、思ってた以上に普通のクラスと同じで、インターフェースを実装できます。
さらには、mainメソッドを書いて実行することもできるのです。

これを定数としてしか使わないのは勿体無い。
ということで、有効活用をちょっと考えてみた。

こういうのはいかが?
package sample;

import java.math.BigDecimal;

enum NumberUtility {

 SIMPLE {

  @Override
  Integer createInteger(String x_str) {
   return Integer.valueOf(x_str);
  }

  @Override
  BigDecimal createBigDecimal(String x_str) {
   return new BigDecimal(x_str);
  }
 },

 QUIET {

  @Override
  Integer createInteger(String x_str) {
   Integer result = null;
   if (x_str != null) {
    try {
     result = SIMPLE.createInteger(x_str);
    } catch (NumberFormatException e) {
    }
   }
   return result;
  }

  @Override
  BigDecimal createBigDecimal(String x_args) {
   BigDecimal result = null;
   if (x_args != null) {
    try {
     result = SIMPLE.createBigDecimal(x_args);
    } catch (NumberFormatException e) {
    }
   }
   return result;
  }
 };

 abstract Integer createInteger(String x_str);

 abstract BigDecimal createBigDecimal(String x_str);
}

abstract class Test {

 void exec() {
  System.out.println("100 => " + getNumberUtility().createInteger("100"));
  System.out.println("10.0 => " + getNumberUtility().createBigDecimal("10.0"));
  System.out.println("null => " + getNumberUtility().createInteger("null"));
 }

 abstract NumberUtility getNumberUtility();
}

public class TestMain {

 public static void main(String[] args) {

  try {
   new Test() {

    @Override
    NumberUtility getNumberUtility() {
     return NumberUtility.SIMPLE;
    }
   }.exec();
  } catch (Exception e) {
   System.out.println("catch exception. => " + e.getMessage());
  }
 
  System.out.println();

  try {
   new Test() {

    @Override
    NumberUtility getNumberUtility() {
     return NumberUtility.QUIET;
    }
   }.exec();
  } catch (Exception e) {
   System.out.println("catch exception. => " + e.getMessage());
  }
 }
}
enumの中にabstractメソッドがあって、その場で実装しています。こんなことができるんですねぇ。知りませんでした。
書いた内容について簡単に言うと、Utilityクラスの切り替えです。
Utilityの処理の役割としてはstaticなメソッドで実装したいのだけど、ロジックの違いをインスタンスで変えたいって感じです。

Javaの言語仕様での、enumの実現方法がシンプルだからこそ、これだけの自由度があるのでしょう。
単純さが自由になります。

1行目でエラーが出ましたと言わせたい

今回は、ちょっとしたネタです。
知っている人も多いかもしれません。

内容はタイトルの通り「ソースコードの1行目でエラーを出す」方法です。
もしかすると、JVMに依存するかもしれませんのであしからず。

エラーを吐くコードを1行目に書く方法
public class ExceptionAtLine1 {public static void main(String[] x_args) { System.out.println(x_args[100]); } }
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
 at ExceptionAtLine1.main(ExceptionAtLine1.java:1)
すぐに思いつく方法です。

いや、そんなの当たり前だろって思われてる人も多いでしょう。

では、以下の方法はどうでしょうか?

Genericsを ”利用して” エラーを吐く方法
package sample;

public class ExceptionAtLine1Zwei {

 public static void main(String[] x_args) {
  IMethod method = new IMethodImpl();
  method.method(Integer.valueOf(100));
 }
}

interface IMethod {
 void method(T x_obj);
}

class IMethodImpl implements IMethod {

 @Override
 public void method(String x_obj) {
  System.out.println(x_obj);
 }
}
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
 at sample.IMethodImpl.method(ExceptionAtLine1Zwei.java:1)
 at sample.ExceptionAtLine1Zwei.main(ExceptionAtLine1Zwei.java:7)
どう見ても呼ばれるべきメソッドがありませんね。
Genericsを中途半端に使用すると恐ろしいことになる良い例です。

Eclipseで警告が出るようにしておきましょう。
厳しさは易しさです。

2011年3月26日土曜日

【修正版】 Struts2 + Spring + Slim3

「Struts2 + Spring + Slim3の連携に挑戦」の修正版です。

前回の内容を修正しました。
改めてSlim3のLocalTransactionとGlobalTransactionについて調べた結果、前回の内容では不完全だったことがわかりました。

今回は完全に書き直しです。

Slim3の新規プロジェクト作成
まず最初に、EclipseのSlim3プラグインを使用してSlim3の新規プロジェクトを作成します。
プロジェクト名とルートパッケージ
Project names3sample
Root Packagecom.brightgenerous.s3.sample.data
不要なファイルを削除しておきます。
削除するファイルとディレクトリ
/src/application_en.properties
/src/application_ja.properties
/war/css
/war/ktrwjr
/war/common.jsp
Jarファイル追加
以下のjarファイルを「war/WEB-INF/lib」に追加します。
追加するjarファイル
aopalliance-1.0
commons-beanutils-1.7.0
commons-digester-2.0
commons-fileupload-1.2.1
commons-io-1.3.2
commons-lang-2.3
commons-logging-1.0.4
commons-validator-1.3.1
freemarker-gae-pre3
javassist
ognl-3.0
spring-aop
spring-beans
spring-context
spring-core
spring-tx
spring-web
struts2-convention-plugin-2.2.1.1
struts2-core-2.2.1.1
struts2-spring-plugin-2.2.1.1
xwork-core-2.2.1.1
freemarker-gae-pre3は、gae用のfreemarkerです。
Springは2.5を使用します。
web.xml
「war/WEB-INF」に配置するweb.xmlファイルです。


 
  slim3.rootPackage
  com.brightgenerous.s3.sample.data
 

 
  contextConfigLocation
  classpath:applicationContext*.xml
 

 
  DatastoreFilter
  org.slim3.datastore.DatastoreFilter
 

 
  struts2
  org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
  
   actionPackages
   com.brightgenerous.s3.sample.action
  
 

 
  DatastoreFilter
  /*
  REQUEST
 
 
  struts2
  /*
 

 
  GlobalTransactionServlet
  org.slim3.datastore.GlobalTransactionServlet
  1
 

 
  GlobalTransactionServlet
  /slim3/gtx
 

 
  org.springframework.web.context.ContextLoaderListener
 
 
  com.brightgenerous.s3.sample.gae.GaeInitListener
 

 
  
   /slim3/gtx
  
  
   admin
  
 


Slim3の新規プロジェクトを作成して生成されるweb.xmlの内容のほとんど少しを消すことになります。
「com.brightgenerous.s3.sample.gae.GaeInitListener」は、独自に実装するクラスです。ソースは「実装するクラス」にて後述します。
applicationContext.xml
「classpath:applicationContext*.xml」(src直下)に配置します。


 

 

 

 
 
  
   
    PROPAGATION_REQUIRED
   
  
 
 
  
  
 
 
  
   
    transactionInterceptor
   
  
  
   
    *ServiceImpl
   
  
 


「transactionManager」のみ用意してやれば他はそのまま使えます。なかなかナイスな設計です。
「transactionManager」と「transactionInterceptor」を用意します。
「com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor」、 「com.brightgenerous.s3.sample.gae.Slim3TransactionManager」、 「com.brightgenerous.s3.sample.gae.Slim3TransactionInterceptor」については、後述です。
実装するクラス
1.com.brightgenerous.s3.sample.gae.GaeInitListener
package com.brightgenerous.s3.sample.gae;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import ognl.OgnlRuntime;

public class GaeInitListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener {

 @Override
 public void contextInitialized(ServletContextEvent sce) {
  OgnlRuntime.setSecurityManager(null);
 }

 @Override
 public void contextDestroyed(ServletContextEvent arg0) {
 }

 @Override
 public void sessionCreated(HttpSessionEvent arg0) {
 }

 @Override
 public void sessionDestroyed(HttpSessionEvent arg0) {
 }

 @Override
 public void attributeAdded(HttpSessionBindingEvent arg0) {
 }

 @Override
 public void attributeRemoved(HttpSessionBindingEvent arg0) {
 }

 @Override
 public void attributeReplaced(HttpSessionBindingEvent arg0) {
 }
}
OGNLのセキュリティマネージャがなんとかかんとか...理由については、詳しく調べていません。
2.com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor
package com.brightgenerous.s3.sample.gae;

public class GaeFixInternalPersistenceAnnotationProcessor {
}
「java.lang.NoClassDefFoundError」を回避することだけが目的です。
3.com.brightgenerous.s3.sample.gae.Slim3TransactionManager
package com.brightgenerous.s3.sample.gae;

import org.slim3.datastore.Datastore;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;

public class Slim3TransactionManager extends AbstractPlatformTransactionManager {

 private static final long serialVersionUID = 3423679590583692519L;

 @Override
 protected void doBegin(Object x_arg0, TransactionDefinition x_arg1) throws TransactionException {
  TransactionObject transactionObject = (TransactionObject) x_arg0;
  transactionObject.setTransaction(Datastore.beginGlobalTransaction());
 }

 @Override
 protected void doCommit(DefaultTransactionStatus x_arg0) throws TransactionException {
  TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();
  transactionObject.getTransaction().commit();
 }

 @Override
 protected Object doGetTransaction() throws TransactionException {
  return new TransactionObject();
 }

 @Override
 protected void doRollback(DefaultTransactionStatus x_arg0) throws TransactionException {
  TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();
  transactionObject.getTransaction().rollback();
 }
}
package com.brightgenerous.s3.sample.gae;

import java.io.Serializable;

import org.slim3.datastore.GlobalTransaction;

public class TransactionObject implements Serializable {

 private static final long serialVersionUID = -8353097480608667182L;

 private GlobalTransaction p_transaction;

 public GlobalTransaction getTransaction() {
  return p_transaction;
 }

 public void setTransaction(GlobalTransaction x_transaction) {
  p_transaction = x_transaction;
 }
}
package com.brightgenerous.s3.sample.gae;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Slim3GlobalTransaction {
}
「org.springframework.jdbc.datasource.DataSourceTransactionManager」の実装を参考にして、Slim3用に簡単に対応させています。
「TransactionObject」、「Slim3GlobalTransaction」も作成します。 このクラスのみ自作です。間違い等あれば指摘していただけるとありがたいです。
4.com.brightgenerous.s3.sample.gae.Slim3TransactionInterceptor
 private void injectTransaction(Object x_obj, TransactionInfo x_txInfo) {
  TransactionStatus txStatus = x_txInfo.getTransactionStatus();
  DefaultTransactionStatus dtxStatus;
  if (!(txStatus instanceof DefaultTransactionStatus)) {
   return;
  }
  dtxStatus = (DefaultTransactionStatus) txStatus;
  Object tx = dtxStatus.getTransaction();
  if (tx instanceof TransactionObject) {
   tx = ((TransactionObject) tx).getTransaction();
  }
  if (!(tx instanceof GlobalTransaction)) {
   return;
  }
  @SuppressWarnings("rawtypes")
  Class clazz = x_obj.getClass();
  do {
   Field[] fields = clazz.getDeclaredFields();
   for (Field field : fields) {
    if (!field.isAccessible()) {
     field.setAccessible(true);
    }
    Slim3GlobalTransaction s3gtx = field.getAnnotation(Slim3GlobalTransaction.class);
    if (s3gtx != null) {
     if (field.getType().isInstance(tx)) {
      try {
       field.set(x_obj, tx);
      } catch (IllegalAccessException e) {
       throw new RuntimeException(e);
      } catch (IllegalArgumentException e) {
       throw new RuntimeException(e);
      }
     }
    }
   }
   clazz = clazz.getSuperclass();
  } while ((clazz != null) && !clazz.equals(Object.class));
 }
「org.springframework.transaction.interceptor.TransactionInterceptor」のソースをコピーしてきて、 上記のメソッドが適当な箇所で呼ばれるように書き換えます。
ライセンスを考慮して全体は公開しませんが、元のソースを読めば変更箇所はわかるはずです。

以上でStruts2 + Spring + Slim3の設定は完了です。
次回こそ、このフレームワークを使用して簡単なサンプルを書いてみます。

2011年3月24日木曜日

Struts2 + Spring + Slim3

この内容は修正されました。
修正後はこちら

Struts2 + Spring + Slim3の連携に挑戦。

ソースの省略はしません。
今回と次回で、簡単なサンプルを動作させるまでの手順を載せます。

Slim3の新規プロジェクト作成
まず最初に、EclipseのSlim3プラグインを使用してSlim3の新規プロジェクトを作成します。
プロジェクト名とルートパッケージ
Project names3sample
Root Packagecom.brightgenerous.s3.sample.data
不要なファイルを削除しておきます。
削除するファイルとディレクトリ
/src/application_en.properties
/src/application_ja.properties
/war/css
/war/ktrwjr
/war/common.jsp
Jarファイル追加
以下のjarファイルを「war/WEB-INF/lib」に追加します。
追加するjarファイル
aopalliance-1.0
commons-beanutils-1.7.0
commons-digester-2.0
commons-fileupload-1.2.1
commons-io-1.3.2
commons-lang-2.3
commons-logging-1.0.4
commons-validator-1.3.1
freemarker-gae-pre3
javassist
ognl-3.0
spring-aop
spring-beans
spring-context
spring-core
spring-tx
spring-web
struts2-convention-plugin-2.2.1.1
struts2-core-2.2.1.1
struts2-spring-plugin-2.2.1.1
xwork-core-2.2.1.1
freemarker-gae-pre3は、gae用のfreemarkerです。
Springは2.5を使用します。
web.xml
「war/WEB-INF」に配置するweb.xmlファイルです。



 
  slim3.rootPackage
  com.brightgenerous.s3.sample.data
 

 
  contextConfigLocation
  classpath:applicationContext*.xml
 

 
  struts2
  org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
  
   actionPackages
   com.brightgenerous.s3.sample.action
  
 

 
  struts2
  /*
 

 
  org.springframework.web.context.ContextLoaderListener
 
 
  com.brightgenerous.s3.sample.gae.GaeInitListener
 


Slim3の新規プロジェクトを作成して生成されるweb.xmlの内容のほとんどを消すことになります。
「com.brightgenerous.s3.sample.gae.GaeInitListener」は、独自に実装するクラスです。ソースは「実装するクラス」にて後述します。
applicationContext.xml
「classpath:applicationContext*.xml」(src直下)に配置します。




 

 

 
 

 
 
 
  
   
    PROPAGATION_REQUIRED
    PROPAGATION_REQUIRED
    PROPAGATION_REQUIRED
    PROPAGATION_REQUIRED,readOnly
   
  
 
 
  
  
 
 
  
   
    transactionInterceptor
   
  
  
   
    *ServiceImpl
   
  
 


「transactionManager」のみ用意してやれば他はそのまま使えます。なかなかナイスな設計です。
「com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor」、「com.brightgenerous.s3.sample.gae.Slim3TransactionManager」については、後述です。
実装するクラス
1.com.brightgenerous.s3.sample.gae.GaeInitListener
package com.brightgenerous.s3.sample.gae;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import ognl.OgnlRuntime;

public class GaeInitListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener {

 @Override
 public void contextInitialized(ServletContextEvent sce) {
  OgnlRuntime.setSecurityManager(null);
 }

 @Override
 public void contextDestroyed(ServletContextEvent arg0) {
 }

 @Override
 public void sessionCreated(HttpSessionEvent arg0) {
 }

 @Override
 public void sessionDestroyed(HttpSessionEvent arg0) {
 }

 @Override
 public void attributeAdded(HttpSessionBindingEvent arg0) {
 }

 @Override
 public void attributeRemoved(HttpSessionBindingEvent arg0) {
 }

 @Override
 public void attributeReplaced(HttpSessionBindingEvent arg0) {
 }
}
OGNLのセキュリティマネージャがなんとかかんとか...理由については、詳しく調べていません。
2.com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor
package com.brightgenerous.s3.sample.gae;

public class GaeFixInternalPersistenceAnnotationProcessor {

 public GaeFixInternalPersistenceAnnotationProcessor() {
 }
}
「java.lang.NoClassDefFoundError」を回避することだけが目的です。
3.com.brightgenerous.s3.sample.gae.Slim3TransactionManager
package com.brightgenerous.s3.sample.gae;

import java.io.Serializable;

import org.slim3.datastore.Datastore;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;

import com.google.appengine.api.datastore.Transaction;

public class Slim3TransactionManager extends AbstractPlatformTransactionManager {

 private static final long serialVersionUID = 3423679590583692519L;

 @Override
 protected void doBegin(Object x_arg0, TransactionDefinition x_arg1) throws TransactionException {
  TransactionObject transactionObject = (TransactionObject) x_arg0;
  transactionObject.p_transaction = Datastore.beginTransaction();
 }

 @Override
 protected void doCommit(DefaultTransactionStatus x_arg0) throws TransactionException {
  TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();
  transactionObject.p_transaction.commit();
 }

 @Override
 protected Object doGetTransaction() throws TransactionException {
  return new TransactionObject();
 }

 @Override
 protected void doRollback(DefaultTransactionStatus x_arg0) throws TransactionException {
  TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();
  transactionObject.p_transaction.rollback();
 }

 private class TransactionObject implements Serializable {

  private static final long serialVersionUID = -8353097480608667182L;

  Transaction p_transaction;
 }
}
「org.springframework.jdbc.datasource.DataSourceTransactionManager」の実装を参考にして、Slim3用に簡単に対応させています。
このクラスのみ自作です。間違い等あれば指摘していただけるとありがたいです。

以上でStruts2 + Spring + Slim3の設定は完了です。
次回は、このフレームワークを使用して簡単なサンプルを書いてみます。

2011年3月22日火曜日

Hack For Japan

先日、3月21日はHack For Japnanに参加してきました。
京都会場のオフラインでの参加です。

今回は、これまでに何度か参加させていただいたHackathonとは趣旨が違いました。
Hackathonでは「お祭りのような感じで、その時間を楽しむ」のですが、 Hack For Japanでは、震災支援という「目的のために動く」のです。

参加を決めるに際して、おそらくは良い経験ができるだろうと思っていましたが、それ以上の貴重な経験がありました。
経験というより、体感です。
私のしょぼい語彙力では表現しきれませんが、あえて書かせてもらうと
「なんかすげー体験したのが良い経験になった」って感じです。
...書かないほうがよかったかな?


正直言って 役に立てませんでした。
以前から感じていたことですが、自分の弱点も明確になりました。
「準備無しに、いきなり走り出すと間違いなく転ぶ」
日ごろの準備が大切です。 そして、Launch and Iterateです。

最後にこれだけは言っておきます。
Hack For Japanは終わってません。始まったばかりです。
私にとってもこれからです。 いったん走り出してしまえば、自分はできる奴...だと思う。

2011年3月21日月曜日

Struts2 + Spring + MyBatis (その2)

Struts2 + Spring + MyBatis (その1)の続きです。

WEBアプリケーション開発で使用するフレームワークをStruts2.2 + Spring2.5 + MyBatis2.3 で構成してみます。
(その2)では、各xmlファイルとpropertiesファイルを揃えます。

必要な設定ファイルを記載しておきます。
(省略)と書かれているファイルの内容については、一例を記述する程度に留まるので説明しません。詳細について知りたい場合は、解説している他サイトを参照することをお勧めします。
設定ファイル
web.xmlServletの配備記述子
struts.xmlStruts2の設定ファイル(省略)
struts.propertiesStruts2のパラメータファイル(省略)
applicationContext.xmlSpringの設定ファイル
detabase.propertiesデータベースのパラメータファイル(省略)
sqlMapConfig.xmlMyBatisの設定ファイル(省略)
tiles.xmlTilesの設定ファイル(省略)
web.xml
「WEB-INF」フォルダ内にある配備記述子です。これが無いと始まりません。
どのプロジェクトでも共通した記述なので、ほぼそのままコピーすれば使えます。
Servlet2.5の構文で書いてみます。



 
  contextConfigLocation
  classpath:applicationContext*.xml
 

 
  struts2
  org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
  
   actionPackages
   sample.action
  
 

 
  struts2
  /*
 

 
  JspSupportServlet
  org.apache.struts2.views.JspSupportServlet
  1
 

 
  JspSupportServlet
  /*
 

 
  org.springframework.web.context.ContextLoaderListener
 
 
  org.apache.struts2.tiles.StrutsTilesListener
 

 
  30
 

 
  index.html
  index.jsp
 

 
  
   http://tiles.apache.org/tags-tiles
   /WEB-INF/tld/tiles-jsp.tld
  
 


Struts2とSpringを使用するには、上記のように記述します。
画面(View)の実装では、JspではなくFreeMarkerを使用します。 FreeMarker内でJspのtaglib(tiles等)を使用するためには、「JspSupportServlet」を動作させておく必要があります。
applicationContext.xml
applicationContex.xmlはweb.xmlに記述した「classpath:applicationContext*.xml」で参照される場所に配置します。
以下の内容は記述例です。



 

 
 

 
  
   
    classpath:database.properties
   
  
 

 
 
  
  
  
  
  
  
 

 
 
  
 
 
  
   
    PROPAGATION_REQUIRED
    PROPAGATION_REQUIRED
    PROPAGATION_REQUIRED
    PROPAGATION_REQUIRED,readOnly
   
  
 
 
  
  
 
 
  
   
    transactionInterceptor
   
  
  
   
    *ServiceImpl
   
  
 

 
 
  
  
 


MyBatis(iBatis)をSpringの管理下に置きます。
Springがトランザクションの管理(Begin、Rollback、Commit等)を、MyBatisがSQLの実行のみを担当するといった役割分担を可能にするためです。
そうすることで、MyBatisをSlim3に置き換えた場合にも同じ設計で実装できます。

大半を省略しましたが、設定ファイルについては以上です。
次回は、Google App Engine上でSlim3と連携させてみます。

2011年3月20日日曜日

Struts2 + Spring + MyBatis (その1)

WEBアプリケーション開発で使用するフレームワークをStruts2.2 + Spring2.5 + MyBatis2.3 で構成してみます。
最終的には、MyBatisをSlim3に置き換えてGoogle App Engine上で動作させるまでを予定しているので、そのための手順も含まれています。
(その1)では、必要なjarファイルの選定をします。

使用するフレームワークとそのバージョンを記載しておきます。
フレームワークとバージョン
Struts2Struts2.2.1.1
SpringSpring2.5.6
MyBatisMyBatis2.3.6

空のWebアプリケーションのプロジェクトを作成しておきます。
「Eclipse IDE for Java EE Developers」の「Dynamic Web Project」から新規作成を行うと簡単です。

Struts2
公式サイトからStruts2.2.1.1をダウンロードします。
ダウンローが完了したらファイルを解凍し、使用するjarファイルを選定してプロジェクトの「WEB-INF/lib」フォルダ内にコピーします。
使用するjarファイル
aopalliance-1.0
commons-beanutils-1.7.0
commons-digester-2.0
commons-fileupload-1.2.1
commons-io-1.3.2
commons-lang-2.3
commons-logging-1.0.4
commons-validator-1.3.1
freemarker-2.3.16
ognl-3.0
struts2-convention-plugin-2.2.1.1
struts2-core-2.2.1.1
struts2-json-plugin-2.2.1.1
struts2-spring-plugin-2.2.1.1
struts2-tiles-plugin-2.2.1.1
tiles-api-2.0.6
tiles-core-2.0.6
tiles-jsp-2.0.6
xwork-core-2.2.1.1
開発を進めていくうちに上記以外のjarファイルが必要になる事がありますが、その時点で追加します。
struts2にはspringのjarファイルも含まれていますが、springのjarファイルは別途用意します。
javassist
source forgeサイトからJavassist3.14をダウンロードします。
ダウンローが完了したらファイルを解凍し、javassist.jarファイルをプロジェクトの「WEB-INF/lib」フォルダ内にコピーします。
Spring
公式サイトからSpring2.5.6をダウンロードします。
ダウンローが完了したらファイルを解凍し、使用するJarファイルを選定してプロジェクトの「WEB-INF/lib」フォルダ内にコピーします。
使用するJarファイル
spring-aop
spring-beans
spring-context
spring-core
spring-tx
spring-web
MyBatis
公式サイトからMyBatis2.3.5をダウンロードします。
ダウンローが完了したらファイルを解凍し、mybatis-2.3.5.jarファイルをプロジェクトの「WEB-INF/lib」フォルダ内にコピーします。

以上でjarファイルの選定は終わりです。
(その2)では、各フレームワークの設定ファイルであるxmlについて書きます。