Tag Archives: SAStruts

[SAStruts]Webアプリケーション用に作成したプロジェクトでスタンドアローンプログラムが実行できない

EclipseのプラグインDoltengからWebアプリケーションとしてSAStrutsを作成し、
開発を進めていたが、mainで動作するスタンドアローンなプログラムを実行させる機会があった。
従来は、プロジェクトを分けているが小さいプログラムだったので、
Webアプリケーションと同一のプロジェクト内にプログラムを作成した。

そこで、スタンドアローンなプログラムを実行。。。エラー発生。
「SingletonS2ContainerFactory.init()」の時点でエラーが発生していた。

Exception in thread "main" org.seasar.framework.container.ComponentNotFoundRuntimeException: [ESSR0046]コンポーネント(interface javax.servlet.ServletContext)が見つかりません
	at org.seasar.framework.container.impl.S2ContainerBehavior$DefaultProvider.acquireFromGetComponentDef(S2ContainerBehavior.java:165)
	at org.seasar.framework.container.impl.S2ContainerBehavior$DefaultProvider.acquireFromGetComponent(S2ContainerBehavior.java:158)
	at org.seasar.framework.container.impl.S2ContainerBehavior.acquireFromGetComponent(S2ContainerBehavior.java:62)
	at org.seasar.framework.container.impl.S2ContainerImpl.getComponent(S2ContainerImpl.java:124)
	at org.seasar.framework.container.SingletonS2Container.getComponent(SingletonS2Container.java:43)
	at org.seasar.struts.util.ServletContextUtil.getServletContext(ServletContextUtil.java:42)
	at org.seasar.struts.util.S2ModuleConfigUtil.getModuleConfig(S2ModuleConfigUtil.java:38)
	at org.seasar.struts.customizer.ActionCustomizer.customize(ActionCustomizer.java:80)
	...
	(省略)

初期設定時に発生したエラーなので、
設定ファイルの内容を編集しなくてはならないことが早い段階で分かった。

上記で起きた問題を解決したので、忘れないために解説する。

以下、解説。

■概要
Webアプリケーション(SAStruts)でプロジェクトした時に、
そのプロジェクトからスタンドアローン(main)の実行を行なう場合、
src/resources以下の設定ファイルをいくつか編集する必要がある。

これを行なわないと、初期起動時にエラーが発生する。

■原因
「SingletonS2ContainerFactory.init()」を呼びだした際に、
スタンドアローンでは使用しないコンポーネント(?)を無駄に読み込む。
これは、プロジェクト作成時にWebアプリケーション用の設定になっているから。

いくつかWebアプリケーション用に定義された値を除去する必要がある。

■解決
編集すべきファイルは、「creator.dicon」。
ここには、初期起動時に呼びだすいくつかのコンポーネントが定義されている。

編集前のcreator.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
	"http://www.seasar.org/dtd/components24.dtd">
<components>
	<include path="convention.dicon"/>
	<include path="customizer.dicon"/>
	<component class="org.seasar.framework.container.creator.ActionCreator"/>
	<component class="org.seasar.struts.creator.FormCreator"/>
	<component class="org.seasar.framework.container.creator.ServiceCreator"/>
	<component class="org.seasar.framework.container.creator.DtoCreator"/>
	<component class="org.seasar.framework.container.creator.InterceptorCreator"/>
	<component class="org.seasar.framework.container.creator.DaoCreator"/>
</components>

編集後のcreator.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
	"http://www.seasar.org/dtd/components24.dtd">
<components>
	<include path="convention.dicon"/>
	<include path="customizer.dicon"/>
	<!-- データベース接続機能のService系のみ残す -->
	<component class="org.seasar.framework.container.creator.ServiceCreator"/>
</components>

これで実行すれば、正常に動作する。
もっと詳細な原因を知りたければ他で調べてください。

■参考URL
http://ml.seasar.org/archives/seasar-user/2009-April/017325.html

[Annotations][TestNG]アノテーションを使用した簡単テストフレームワーク「TestNG」

最近SAStrutsをよく使用するため、アノテーション機能も使用する機会が多くなった。
その中でもTestNGというテスト用フレームワークは、すごく使いやすい。

■Eclipseにプラグインとして追加
以下のURLに書いてあるようにプラグインの追加を行なう。
http://testng.org/doc/download.html

Eclipseの再起動後、プロジェクトを作成。
プロジェクトを右クリック→プロパティ→Javaビルドパス→ライブラリへ移動
外部Jarの追加で「testng-jdk15.jar」を追加で完了

■テストされるクラスの作成
テストされるクラスを作成する。
ポイントは3点。
1.コンストラクタで引数を受け取ること。
2.コンストラクタで受け取った値を引数なしでreturnする。
3.引数とコンストラクタで受け取った値を結合してreturnする。

Sample.java

public class Sample {
	private String value;

	public Sample(String value) {
		this.value = value;
	}

	public String str() {
		return this.value;
	}

	public String str(String str) {
		return str + "_" + this.value;
	}
}

■テストクラスの作成
テスト用クラスを作成する。
今回は、引数なしのメソッドと引数ありのメソッドの2種類を作成するが、
ひとまず、引数なしのメソッドだけテストするクラスを作成する。

SampleTest.java

import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

/* これ重要 */
import static org.testng.Assert.*;

public class SampleTest {
	private Sample sample;

	/** BeforeClassを定義するとテスト処理の前にこのメソッドが呼ばれる */
	@BeforeClass
	public void init() {
		this.sample = new Sample("test value");
	}

	/** 引数なしのテスト処理 */
	@Test
	public void sysout() {
		String result = this.sample.str();

		/* resultがnullではないか */
		assertNotNull(result);
		/* resultが第2引数と一致しているか */
		assertEquals(result, "test value");
	}
}

■テスト実行、確認
前項で作成したテストクラスを右クリック→実行(Run As)→TestNG Testをクリック
コンソールに実行結果がでているがひとまず無視。

プロジェクト名を右クリック→更新(Refresh)をクリック
すると、「test-output」と「temp-testng-customsuite.xml」が勝手に出力されているはず。

「test-output」は、テスト結果をHTMLとして出力してくれるため、
非常に便利。

それと「temp-testng-customsuite.xml」は、
テストを実行する際の設定ファイルになる。

■引数を渡したテストクラスの作成
先ほど作成したSample.javaの引数ありメソッドをテストする。
アノテーションParametersが2箇所入ってる。
このParametersは次項で設定ファイルに追記する。

SampleTest.java

import org.testng.annotations.BeforeClass;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

/* これ重要 */
import static org.testng.Assert.*;

public class SampleTest {
	private Sample sample;

	/** BeforeClassを定義するとテスト処理の前にこのメソッドが呼ばれる */
	@BeforeClass
	@Parameters("const-value")
	public void init(String constValue) {
		this.sample = new Sample(constValue);
	}

	/** 引数なしのテスト処理 */
	@Test
	public void sysout() {
		String result = this.sample.str();

		/* resultがnullではないか */
		assertNotNull(result);
		/* resultが第2引数と一致しているか */
		assertEquals(result, "test value");
	}

	/** 引数ありのテスト処理 */
	@Test
	@Parameters("test-value")
	public void sysout(String str) {
		String result = this.sample.str(str);

		/* resultがnullではないか */
		assertNotNull(result);
	}
}

■設定ファイルを編集
解説も面倒になってきたので、設定ファイルを見て
あとは本家サイトで記述方法を学んでください。

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="LoginSample">
	<!-- テストクラス内で使用するパラメータ系を定義する -->
	<parameter name="const-value" value="test value" />
	<parameter name="test-value" value="XXXXXXX" />

	<!-- nameは必須項目。通常パッケージ名などでカテゴリ分けする -->
	<test name="defaultPackage">
		<classes>
			<!-- テストするクラスを指定 -->
			<class name="SampleTest" />
		</classes>
	</test>
</suite>

[SAStruts]テスト時(HotDeploy)と本番時(CoolDeploy)の切り替え

前もって言っておくが、
すでにSAStrutsを使用している人は理解できる話なので、
読まなくてよい。

今回は、HotDeployとCoolDeployの話。

これは、SAStrutsを最近導入した人やこれから導入する人には、
少々理解しづらい。私もその一人だった。

ここから解説。

HotDeploy時は、テスト時に使用するモード。
1スレッドしか使用できないため、複数の接続を同時に処理できない。
あくまでも一人でのテスト用に使用する。

CoolDeploy時は、本番時に使用するモード。
複数のアクセスを可能にする。

WarmDeployというモードもあるが省略。

2つのモードを切り替えるには、env.txtを編集する。
たぶんデフォルトでは「ct(ホットデプロイ)」と書かれているが、
これを「product(クールデプロイ)」に変更する。

詳細は、以下のURLで解説している。

http://sastruts.seasar.org/fileReference.html#env

ここでなかなか難しいのが、
HotDeployでは動いていたが、CoolDeployに切り替えると
動かなくなる(エラーがでる)という現象。

Seasar2側からすれば
「CoolDeployで動かないんだからちゃんと書け」
と言うだろうが、こっちからすれば
「HotDeployで動いてるんだからちゃんと動け」って話。

仕方ない。

[SAStruts][Interceptor]セッションによるログイン認証を可能にする

Webアプリケーションだと、セッション(Session)でログイン情報を保持する方法をよくとる。
今回は、SAStrutsでログイン処理+セッション保持を紹介する。
たぶん、このやり方が一般的かと思う。

■はじめに
今回、Eclipse→Dolteng→SAStruts→S2JDBCで開発するので、その辺を注意。
プロジェクト名が「LoginSample」とする。
ベースURLが「http://localhost:8080/LoginSample/」とする。
ベースパッケージが「biz.mikuriya」とする。

■大まかな手順
いくつかのプログラム開発、設定ファイル編集を行なうと、
ログイン処理+セッション保持が可能になる。
以下の手順で作業を進める。

・ユーザーデータクラスを作成
・アクションフォームクラスを作成
・アクション(ページ構成)を作成
・アクションに合ったJSPを作成
・ログイン処理+セッション保持処理用のクラスを作成
・customizer.diconを編集
・動作確認

■ユーザーデータクラスを作成
メールアドレスだけをセッションに保持しておきたいので、
ユーザーデータクラスは、以下のように作成。

biz.mikuriya.dto.UserDto.java

package biz.mikuriya.dto;

import java.io.Serializable;

import org.seasar.framework.container.annotation.tiger.Component;
import org.seasar.framework.container.annotation.tiger.InstanceType;

@Component(instance = InstanceType.SESSION)
public class UserDto implements Serializable {
	private static final long serialVersionUID = 1L;

	public String email;
}

■アクションフォームクラスを作成
メールアドレスとパスワードの入力によるログイン処理が行なわれるため、
それ用の入力チェックデータクラスを作成。

biz.mikuriya.form.UserForm.java

package biz.mikuriya.form;

import org.seasar.struts.annotation.EmailType;
import org.seasar.struts.annotation.Maxlength;
import org.seasar.struts.annotation.Minlength;
import org.seasar.struts.annotation.Required;

public class UserForm {
	@Required
	@EmailType
	public String email;

	@Required
	@Minlength(minlength = 6)
	@Maxlength(maxlength = 32)
	public String password;
}

■アクション(ページ構成)を作成
今回は3ページ作成する。
トップページ、ログインページ、マイページの3種類を作成。

biz.mikuriya.action.IndexAction.java(トップページ)

※ログインの必要がないページ

package biz.mikuriya.action;

import javax.annotation.Resource;

import org.seasar.struts.annotation.Execute;

import biz.mikuriya.dto.UserDto;

public class IndexAction {
	@Resource
	public UserDto userDto;

    @Execute(validator = false)
	public String index() {
        return "index.jsp";
	}
}

biz.mikuriya.action.LoginAction.java(ログインページ)

※ログインの必要がないページ

package biz.mikuriya.action;

import javax.annotation.Resource;

import org.seasar.framework.beans.util.Beans;
import org.seasar.struts.annotation.ActionForm;
import org.seasar.struts.annotation.Execute;

import biz.mikuriya.dto.UserDto;
import biz.mikuriya.form.UserForm;

public class LoginAction {
	@Resource
	@ActionForm
	public UserForm userForm;

	@Resource
	protected UserDto userDto;

	@Execute(validator = true, input = "/login/index.jsp")
	public String index() {
		// セッションへの保存処理
		// userDtoに値を入れるだけでSessionに保存される
		Beans.copy(this.userForm, this.userDto).execute();

		return "/mypage/?redirect=true";
	}
}

biz.mikuriya.action.MypageAction.java(マイページ)

※ログインが必要なページ

package biz.mikuriya.action;

import javax.annotation.Resource;

import org.seasar.struts.annotation.Execute;

import biz.mikuriya.dto.UserDto;

public class MypageAction {
	@Resource
	public UserDto userDto;

	@Execute(validator = false)
	public String index() {
		return "index.jsp";
	}
}

■アクションに合ったJSPを作成
前項で作成したActionに一致したJSPファイルを作成。

/WEB-INF/view/index.jsp

<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>トップページ</title>
</head>
<body>
	<h1>トップページ</h1>

	メールアドレス<br/>
	ここに表示「<b>${userDto.email}</b>」<br/>

	<br/>

	<a href="<c:url value="/"/>">トップページ</a>|<a href="<c:url value="/login/"/>">ログインページ</a>|<a href="<c:url value="/mypage/"/>">マイページ</a>
</body>
</html>

/WEB-INF/view/login/index.jsp

<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>ログインページ</title>
</head>
<body>
	<h1>ログインページ</h1>

	<html:errors/>

	<s:form action="/login/">
		メールアドレス<html:text property="email" /><br/>
		パスワード<html:password property="password" /><br/>
		<br/>
		<html:submit value="ログイン"/>
	</s:form>

	<br/>

	<a href="<c:url value="/"/>">トップページ</a>|<a href="<c:url value="/login/"/>">ログインページ</a>|<a href="<c:url value="/mypage/"/>">マイページ</a>
</body>
</html>

/WEB-INF/view/mypage/index.jsp

<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>マイページ</title>
</head>
<body>
	<h1>マイページ</h1>

	<b>これがマイページ</b><br/>

	メールアドレス<br/>
	ここに表示「<b>${userDto.email}</b>」<br/>

	<br/>

	<a href="<c:url value="/"/>">トップページ</a>|<a href="<c:url value="/login/"/>">ログインページ</a>|<a href="<c:url value="/mypage/"/>">マイページ</a>
</body>
</html>

■ログイン処理+セッション保持処理用のクラスを作成
重要ポイントとなるログイン処理クラスを作成。

biz.mikuriya.interceptor.LoginConfirmInterceptor.java

package biz.mikuriya.interceptor;

import javax.annotation.Resource;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.interceptors.AbstractInterceptor;
import org.seasar.struts.annotation.Execute;

import biz.mikuriya.dto.UserDto;

public class LoginConfirmInterceptor extends AbstractInterceptor {
	private static final long serialVersionUID = 1L;

	@Resource
	protected UserDto userDto;

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// ここの条件がtrueであればログイン済みと判断
		// ここの条件がfalseであればログインページへ移動
		return (!isExecuteMethod(invocation) || isLoggedIn()) ? invocation
				.proceed() : "/login/";
	}

	private boolean isExecuteMethod(MethodInvocation invocation) {
		return invocation.getMethod().isAnnotationPresent(Execute.class);
	}

	private boolean isLoggedIn() {
		// Sessionにユーザー情報が登録されているかチェック
		return (userDto != null && userDto.email != null);
	}
}

■customizer.diconを編集
これも重要ポイントとなるログイン処理を有効にするための設定ファイル
これを編集する。
MypageActionだけをセッションチェックしたいので、addClassPatternで登録する

customizer.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
	"http://www.seasar.org/dtd/components24.dtd">
<components>
	<include path="default-customizer.dicon"/>
	<component name="formCustomizer" class="org.seasar.framework.container.customizer.CustomizerChain"/>

	<component name="actionCustomizer" class="org.seasar.framework.container.customizer.CustomizerChain">
		<initMethod name="addAspectCustomizer">
			<arg>"aop.traceInterceptor"</arg>
			<!-- ここ重要(追加必須) -->
			<arg>true</arg>
		</initMethod>

		...(中略)...

		<!-- SESSIONのチェック設定はここから -->
		<initMethod name="addCustomizer">
			<arg>
				<component class="org.seasar.framework.container.customizer.AspectCustomizer">
					<property name="interceptorName">"loginConfirmInterceptor"</property>
					<!-- ここ重要(追加必須) -->
					<property name="useLookupAdapter">true</property>

					<!-- このActionだけセッションチェックを行なう -->
					<initMethod name="addClassPattern">
						<arg>"biz.mikuriya.action"</arg>
						<arg>"MypageAction"</arg>
					</initMethod>
				</component>
			</arg>
		</initMethod>
		<!-- SESSIONのチェック設定はここまで -->
	</component>

	...(省略)...
</components>

「ここ重要」とコメントにした部分は、とても重要。
以下のURLに書かれているように、HotDeploy時とCoolDeploy時では、
動作が違うので、注意すべし。

[S2Container][SAStruts] セッション格納の値について

■動作確認
実際に動作を確認する。

http://localhost:8080/LoginSample/

セッションがない状態でマイページへ接続

http://localhost:8080/LoginSample/mypage/

[SAStruts]S2JDBCでJOINのやり方をいつも忘れるからメモ

SAStrutsでJOINを行なう際、どう書くか毎回忘れてしまう。

単純なテストを以下に記す。



■データベースのスキーマ

CREATE DATABASE db_test;
USE db_test;

CREATE TABLE channels (
	channel_id INTEGER NOT NULL AUTO_INCREMENT
	, title TEXT NOT NULL
	, PRIMARY KEY (channel_id)
);

CREATE TABLE items (
	item_id INTEGER NOT NULL AUTO_INCREMENT
	, title TEXT NOT NULL
	, url VARCHAR(255) NOT NULL
	, channel_id INTEGER NOT NULL
	, PRIMARY KEY (item_id)
	, UNIQUE (url)
);

INSERT INTO channels (title) VALUES("情報考学 Passion For The Future");
INSERT INTO channels (title) VALUES("RwJ");

INSERT INTO items (title,url,channel_id) VALUES("無趣味のすすめ","http://www.ringolab.com/note/daiya/2009/07/post-1021.html",1);
INSERT INTO items (title,url,channel_id) VALUES("アイデア・スイッチ 次々と発想を生み出す装置","http://www.ringolab.com/note/daiya/2009/07/post-1023.html",1);
INSERT INTO items (title,url,channel_id) VALUES("ベガーズ・イン・スペイン","http://www.ringolab.com/note/daiya/2009/07/post-1022.html",1);
INSERT INTO items (title,url,channel_id) VALUES("psコマンドはオプションに「-」がいらないみたい","http://blog.mikuriya.biz/archives/310",2);
INSERT INTO items (title,url,channel_id) VALUES("[SAStruts]Tomcat起動時に初回のみ呼び出す独自のクラスでデータベース接続","http://blog.mikuriya.biz/archives/171",2);
INSERT INTO items (title,url,channel_id) VALUES("[PHP]フレームワーク「CakePHP」のインストールと設定","http://blog.mikuriya.biz/archives/7",2);




■jdbc.diconを編集

今回は、MySQLを使用するので、そのあたりを編集する。

<components namespace="jdbc">
	...

	<component name="xaDataSource"
		class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
		<property name="driverClassName">
			"com.mysql.jdbc.Driver"
		</property>
		<property name="URL">
			"jdbc:mysql://localhost:3306/db_test"
		</property>
		<property name="user">"root"</property>
		<property name="password">""</property>
	</component>
	...
</components>




■channelsテーブルのクラス

package biz.mikuriya.entity;

import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "channels")
public class Channel {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public int channelId;

	public String title;

	@OneToMany(mappedBy = "parent") // Itemに定義する変数名がparentになる
	public List<Item> childs;
}




■itemsテーブルのクラス

package biz.mikuriya.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "items")
public class Item {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public int itemId;

	public String title;

	public String url;

	public int channelId;

	@ManyToOne
	@JoinColumn(name = "channel_id")
	public Channel parent; // Channel.itemsのmappedByの値と変数名を合わせる
}




■動作テスト用実行クラス

package biz.mikuriya;

import java.util.List;

import org.seasar.extension.jdbc.JdbcManager;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

import biz.mikuriya.entity.Channel;
import biz.mikuriya.entity.Item;

public class DBTest {
	private JdbcManager jdbcManager;

	/**
	 * データベース接続
	 */
	public DBTest() {
		SingletonS2ContainerFactory.init();
		S2Container container = SingletonS2ContainerFactory.getContainer();
		this.jdbcManager = (JdbcManager) container
				.getComponent(JdbcManager.class);
	}

	/**
	 * 普通にSELECT
	 */
	public void selectChannel() {
		System.out.println("CHANNELS************************************");

		List<Channel> channels = this.jdbcManager.from(Channel.class)
				.getResultList();

		for (Channel channel : channels) {
			System.out.println("\t" + channel.channelId + "\t" + channel.title);
			System.out.println("\t--------");
		}
	}

	/**
	 * 普通にSELECT
	 */
	public void selectItem() {
		System.out.println("ITEMS************************************");

		List<Item> items = this.jdbcManager.from(Item.class).getResultList();

		for (Item item : items) {
			System.out.println("\t" + item.itemId + "\t" + item.channelId
					+ "\t" + item.title + "\t" + item.url);
			System.out.println("\t--------");
		}
	}

	/**
	 * 一対多の構造で取得する場合
	 */
	public void joinOneToMany() {
		System.out.println("JOIN************************************");

		List<Channel> channels = this.jdbcManager.from(Channel.class)
				.innerJoin("childs").getResultList(); // JOINには、変数名を指定する

		for (Channel channel : channels) {
			System.out.println("\t" + channel.channelId + "\t" + channel.title);

			for (Item item : channel.childs) {
				System.out.println("\t\t" + item.itemId + "\t" + item.channelId
						+ "\t" + item.title + "\t" + item.url);
				System.out.println("\t\t--------");
			}

			System.out.println("\t--------");
		}
	}

	/**
	 * 一対一の構造で取得する場合
	 */
	public void joinOneToOne() {
		System.out.println("JOIN************************************");

		List<Item> items = this.jdbcManager.from(Item.class)
				.innerJoin("parent").getResultList(); // JOINには、変数名を指定する

		for (Item item : items) {
			System.out.println("\t" + item.itemId + "\t" + item.channelId
					+ "\t" + item.title + "\t" + item.url + "\t"
					+ item.parent.channelId + "\t" + item.parent.title);
			System.out.println("\t--------");
		}
	}

	public static void main(String[] args) {
		DBTest test = new DBTest();

		test.selectChannel();
		test.selectItem();

		test.joinOneToMany();
		test.joinOneToOne();
	}
}

[SAStruts]Tomcat起動時に初回のみ呼び出す独自のクラスでデータベース接続

SAStruts(Super Agile Struts)の開発、設定方法に苦戦した。



やりたいことは以下。

1.WebサーバTomcat起動時に独自に作成したクラスを呼び出したい。

2.呼び出したクラス内でデータベースにSELECTを発行し、取得したレコードをメモリに読み込みたい。



1点目の解決法は、customizer.diconにinitializeServiceを設定すれば解決できる。

(customizer.diconへの設定例)
<components>
...(中略)
    <component name="initializeService" class="jp.mikuriya.Init">
        <initMethod name="init">
            <arg>"/home/hoge/sample/property/sys.properties"</arg>
        </initMethod>
    </component>
<components>


(クラスの記述例)
package jp.mikuriya;

import org.seasar.extension.jdbc.JdbcManager;

public class Init {
    public JdbcManager jdbcManager;

    public void init(String propertyFile) {
        /* -------------------- */
        /* 適当な処理内容を記述 */
        /* -------------------- */
        System.out.println("Tomcat起動時に呼ばれたよ。[propertyFile=" + propertyFile + "]");
    }
}

Tomcatを起動すればこれでSystem.outは実行される。


しかし、上記の「クラスの記述例」で記述している「JdbcManager jdbcManager」に注目してもらいたい。

フィールドに設定しておくと、勝手にデータベースに接続してくれる

SAStrutsの特徴であるJdbcManagerが記述されている。

これがどうにも上手くいかない。

うまくいかないとはデータベースに接続できないってこと。



Tomcatのログ(catalina.out)を確認すると、以下のようなメッセージが出力されている。

「...プロパティ(jdbcManager)が見つからないので設定をスキップします」

Tomcatのログをもっと確認してみると、customizer.diconを呼び出しているようなログが

s2jdbc.diconを呼び出しているようなログよりも前の行に表示されている。

ってことは、s2jdbc.diconを呼び出してから、例題のプログラムを起動させればよいと考えた。



そこで先ほどcustomizer.diconに設定していた内容を切り取って、

s2jdbc.diconに追加する。

(s2jdbc.diconへの設定例)
<components>
...(中略)
    <component name="initializeService" class="jp.mikuriya.Init">
        <initMethod name="init">
            <arg>"/home/hoge/sample/property/sys.properties"</arg>
        </initMethod>
    </component>
</components>



これで再度Tomcatのログを確認してみると、s2jdbc.diconを呼び出した後に

System.outが出力されているのがわかる。



他にやり方がありそうなのだが、わかんねぇ。