2010/02/15

Wicketでページテンプレートを利用

Apache WicketStrutsのTilesのように画面のレイアウトを共通化する方法を調べてみました。
(via Apache Wicket -> Examples -> Markup inheritance

1. BasePage.html
まずベースとなるHTMLを作成します。ここではBasePage.htmlとします。




ここに本文が入ります。




ポイントとなるのは<wicket:child/>です。
このタグで囲った部分が各コンテンツによって差し替えられますので、上記の場合だとヘッダーとフッターはこのままで、ボディの部分がコンテンツによって変化することになります。

2. BasePage.java
次にBasePage.htmlのページクラスを作成します。

/**
* 共通テンプレート用のページクラスです。
*
* @author namiki
*
*/
public abstract class BasePage extends WebPage {
public BasePage() {
}
public BasePage(final PageParameters params) {
}
}

見ての通り特に何もしていません。今後、ヘッダーやフッターに共通の機能を埋め込む場合は必要となりますが、今のところは何もしないままにしておきます。

3. SubPage.html
次にBasePage.htmlにコンテンツを埋め込む側のSubPage.htmlを作成します。




テンプレートを利用したページのサンプルです。






ポイントとなるのは<wicket:extend/>です。
BasePage.htmlの<wicket:child/>で囲まれた部分に、SubPage.htmlの<wicket:extend/>で囲まれた部分が埋め込まれます。ここでは独自のヘッダーやフッターを定義していますが、実際に動作させた場合はBasePage.htmlのヘッダー・フッターが利用されます。

4. SubPage.java
最後にSubPage.htmlのページクラスを作成します。このクラスではBasePage.javaを継承する必要があります。それによってBasePage.htmlとSubPage.htmlの継承関係を表します。

/**
* SubPage.html用のページクラスです。
*
* @author namiki
*
*/
public class SubPage extends BasePage {
public HelloWicketPage(final PageParameters params) {
add(new Label("messages", "Hello, wicket world!!"));
}
}

これで画面レイアウトを共通化することができました。

2010/02/13

車の点検

ライフくんがイグナイター交換後かなり調子を取り戻してきてくれましたが、念には念を入れてディーラーさんで12ヶ月点検を行ってもらいました。

エンジン内部についてはメインリレーとイグニッションスイッチがだいぶ古くなっているので交換、足回りもアウトボードブーツがだいぶ痛んでいるようなので交換した方が良いと勧められたため、再来週にもう一度パーツ交換で入院です。

車体自体はだいぶ安く譲ってもらったものの、なんだかんだでお金が掛かってしまいましたが、まぁこんなものでしょう。これでしばらくは安心して乗れる、はず。

2010/02/09

Wicket on GoogleAppEngine

GoogleAppEngine(以下、GAE)上で動作するWebアプリフレームワークを調べています。Apache Strutsに関しては特に苦労することなく動作させることができたので、次はApache Wicketに挑戦してみます。

1. appengine-web.xml
appengine-web.xmlを編集して、セッションを利用可能にします。appengine-web.xmlに以下の内容を追記します。

true


2. web.xml
web.xmlの内容は以下の通りです。


xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

wicket.yonko
org.apache.wicket.protocol.http.WicketFilter

applicationClassName
net.masa.wicket.YonkoWicketApplication



wicket.yonko
/*




3. WicketApplication
次にWicketApplicationクラスを作成します。GAEでWicketを動作させる場合、以下のポイントがあります。

  • WicketApplication#newSessionStore()でHttpSessionStoreを返すようにさせる

  • WicketをDEPLOYMENTモードで動作させる

  • リソースの更新チェックを停止させる


Wicketのデフォルト設定では、セッションオブジェクトをファイルに保存します。が、GAEではファイルの作成は制限されていますので、HttpSessionに保存するように設定します。
次にWicketのdevelopmentモードではThreadを生成してリソースの更新チェックを行いますが、GAEではThreadの生成を制限しているため、deploymentモードで動作させる必要があります。これにより、Threadの生成を停止できます。
上記のポイントを踏まえ、実際のソースは以下の通りとなります。

/**
* GAE/j用WicketApplicationクラスです。
*
* @author namiki
*
*/
public class YonkoWicketApplication extends WebApplication {

/** リクエスト・レスポンスおよびHTMLのエンコードです。 */
private static final String ENCODE = "UTF-8";

/** HTMLテンプレートファイルのベースフォルダです。 */
private static final String RESOURCE_FOLDER = "WEB-INF";

/** HTMLテンプレートファイルの本来の階層から除去するパスです。 */
private static final String PAGES_PATH = "net/masa/wicket";

/**
* コンストラクタです。
*/
public YonkoWicketApplication() {
}

/*
* (non-Javadoc)
*
* @see org.apache.wicket.Application#getHomePage()
*/
@Override
public Class getHomePage() {
return HelloWicketPage.class;
}

/**
* Wicketの動作モードを設定します。

* WicketをGAE/jで動作させる場合、{@link org.apache.wicket.Application.DEPLOYMENT}
* モードで動作する必要があります。
*
* @see org.apache.wicket.protocol.http.WebApplication#getConfigurationType()
*/
@Override
public String getConfigurationType() {
return Application.DEPLOYMENT;
}

/**
* 初期化処理です。
*
* @see org.apache.wicket.protocol.http.WebApplication#init()
*/
@Override
protected void init() {
super.init();
// リクエスト・レスポンスの文字コード設定
getRequestCycleSettings().setResponseRequestEncoding(ENCODE);
// HTMLテンプレートの文字コード設定
getMarkupSettings().setDefaultMarkupEncoding(ENCODE);
// リソースの更新チェックを停止
getResourceSettings().setResourcePollFrequency(null);

// テンプレートファイルの場所を指定
getResourceSettings().addResourceFolder(RESOURCE_FOLDER);
getResourceSettings().setResourceStreamLocator(
new ResourceStreamLocator() {
@SuppressWarnings("unchecked")
@Override
public IResourceStream locate(Class clazz, String path) {
if (path.indexOf(PAGES_PATH, 0) != -1) {
IResourceStream located = super.locate(clazz, path
.substring(PAGES_PATH.length() + 1));
if (located != null) {
return located;
}
}
return super.locate(clazz, path);
}
});

}

/**
* セッションストアを生成します。

* WicketをGAE/jで動作させる場合、セッション情報を{@link javax.servlet.http.HttpSession}
* に格納する必要があります。
*/
@Override
protected ISessionStore newSessionStore() {
return new HttpSessionStore(this);
}
}

上記init()内でHTMLテンプレートファイルの場所を指定しています。Wicketは通常の場合クラスファイルと同じ階層からHTMLテンプレートファイルを探しますが、RESOURCE_FOLDER(=WEB-INF)から探すように指定しています。また、PAGES_PATH(=net/masa/wicket/)は、パッケージ名から除去すべきパスを定義しています。よって、これらの設定からWicketはHTMLテンプレートファイルをWEB-INF/pagesから探すようになっています。

4. HelloWicketPage
次にWicketApplicationクラスが最初に呼び出すページを作成します。最初に呼び出すページはApplication#getHomePage()で定義しますが、上記の例ではHelloWicketPage.classを呼び出していますのでそのクラスを作成します。パッケージはnet.masa.wicket.pagesです。

/**
* 初回表示用画面クラスです。
*
* @author namiki
*
*/
public class HelloWicketPage extends WebPage {
public HelloWicketPage(final PageParameters params) {
add(new Label("messages", "Hello, wicket world!!"));
}
}

ここではHTMLに表示するメッセージを定義しています。
次にこのメッセージを表示するためのHTMLテンプレートファイルを作成します。
${project}\war\WEB-INF\pagesというディレクトリを作成し、その直下にHelloWicketPage.htmlを作成します。



これで準備が整いましたのでEclipseから起動して、http://localhost:8888にアクセスしてみると、画面にはHelloWicketPage.javaで定義したメッセージが表示されます。

これでGAE上でWicketを動作させることができました。今後はこれをベースにして色々な機能を追加していこうと思います。

2010/02/08

Blogger SyntaxHighlighterの導入

SyntaxHighlighterは、ブログ上のソースコードの文法部分をハイライトしてくれるフリーウェアです。はてなダイアリーの場合は特に問題ありませんでしたが、blogger移転を機に少し調べてみました。

ここではBlogger向けのWigetとして提供されているFaziBear's Blogger Widgetsを使ってみます。
導入方法は、以下のURLにアクセスして、「Add to Blogger」をクリックするだけ。
FaziBear's Blogger Widgets
このブログにも導入してみたので、正常に動作するか以下のJavaコードで確認してみます。

public class HelloMain {
public static void main(String[] args) {
System.out.println("hello, world.");
}
}

どうやら正常に動作しているようですね。対応しているプログラム言語はこちらで公開されていますので、Java以外の言語でも大丈夫なようです。

今回は簡単のためにFaziBear's Blogger Widgetsを導入してみましたが、こちらはSyntaxHighlighterのバージョンが1.5.1と少し古く、最新版がSyntaxHighlighterで公開されているので、近いうちに最新版を導入してみようと思います。

2010/02/07

クルマ

ここ最近トラブル続きで非常に苦しんでいました。
駐車場でエンジンが掛からないのに始まり、コンビニの駐車場でエンジンが掛からなかったり、彼女を送っていったはいいけど、その場でエンジン掛からなかったり、挙句には西湘BPの出口で止まってしまったり。
都合3回ほどレッカーのお世話になってしまいました。

が、それも昨日修理工場の方に見て頂き、やっと原因が判明しました。どうやらイグナイターというパーツがダメになってしまっているとのこと。新品だと結構高価なので、正常動作しているモノを中古から取ってきて交換するみたいです。

自分のライフはJA4というエンジン型なんですが、この型はイグナイターが結構壊れやすいらしいです。どうやらデスビがエンジンに直付けとなっているため、熱ですぐにヤラれてしまうみたい。イグナイターの他にも、イグニッションスイッチやメインリレーなども注意が必要です。

イグナイター交換が終わったら近所のディーラーに持ち込んで一通り点検してもらおうと思います。

追記:
今日、14時過ぎに修理工場の方が来てくれ、早速パーツ交換してくれました。交換したパーツは、デスビ、イグナイター、イグニッションコイルとのこと。工賃込みで20k。まだ試走してないけど、エンジンの始動が安定した気がします。

2010/02/04

WTPでTomcatのJNDIを利用する

環境はEclipse3.4.2、WTP3.0.4、Tomcat5.5.27。プロジェクトはMavenのmaven-archetype-webappで作成しました。DBはSQLServer2005です。
まずはsrc/main/webapp/META-INF配下にcontext.xmlを作成します。context.xmlの内容は以下の通り。



auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
url="jdbc:sqlserver://127.0.0.1:1433;databaseName=pj;"
username="sa"
password="pj"
maxActive="20"
maxIdle="10"/>


上記のContext要素の内容は、Servers -> Tomcat v5.5 Server at localhost-config -> server.xmlからコピーしました。

次にsrc/main/webapp/WEB-INF/web.xmlに以下の内容を追加します。


SQLServer2005 DataSource
jdbc/sample
javax.sql.DataSource
Container
Shareable


最後に%CATALINA_HOME%\common\lib配下にJDBCドライバのjarファイルを放り込むと、WTPで起動したTomcatでもJNDIによるDataSourceの取得を行うことができます。

ちなみにWTPでTomcatを設定した場合、以下のディレクトリをTomcat環境と見立ててServerを起動します。

  • workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0

2010/02/03

SQLServerで自動生成キーの取得

SQLServer2005で、IDENTITY値を利用したテーブルに対してInsertを行った場合に、生成されたキーを取得する方法です。いまのプロジェクトではdbutilsを利用しているので、QueryRunnerをオーバーライドして、専用のQueryRunnerを作成しました。

public class GeneratedKeysReturnQueryRunner extends QueryRunner {

@Override
public int update(Connection conn, String sql, Object[] params)
throws SQLException {
PreparedStatement stmt = null;
ResultSet rs = null;
ResultSetMetaData meta = null;
int key = 0;
try {
stmt = this.prepareStatement(conn, sql);
this.fillStatement(stmt, params);
stmt.executeUpdate();
rs = stmt.getGeneratedKeys();
meta = rs.getMetaData();
if (rs.next()) {
key = rs.getInt(meta.getColumnCount());
}
} catch (SQLException e) {
this.rethrow(e, sql, params);
} finally {
close(stmt);
}
return key;
}

@Override
protected PreparedStatement prepareStatement(Connection conn, String sql)
throws SQLException {
return conn.prepareStatement(sql,
PreparedStatement.RETURN_GENERATED_KEYS);
}
}

J2SE1.4以上がサポートしているJDBC 3.0 APIにある、PreparedStatement.RETURN_GENERATED_KEYSを利用するのがポイントです。PostgreSQLやMySQL、Oracleで同じようなことが出来るのかは未検証です。
参考:MSDN 自動生成キーの使用