Top > MyPage
 

知久のstrutsによるHello World

ダウンロード&インストール

Javaのインストール

一応1.5系を<URL:http://java.sun.com/javase/downloads/index_jdk5.jsp>からアーカイブをダウンロード、そしてインストール。

Tomcatのダウンロード

6系を<URL:http://tomcat.apache.org/download-60.cgi>から、ダウンロード。これを適当に解凍(windows版のインストーラー付きもあるので、とっても簡単)。

起動して、http://localhost:8080/にアクセスして、管理画面が出ていればOK。

Eclipseのインストール

<URL:http://amateras.sourceforge.jp/cgi-bin/fswiki/wiki.cgi?page=AmaterasIDEInstaller>

から「アマテラス」をダウロード。実行すれば、インストール終了。ただ、インストールディレクトリーをC:\AmaterasIDE_2.0.5.1にした(一応スペースは避けて、デフォルトのスペースをアンスコにしておいた---あまり意味はない)。

Eclipseのプラグイン

あると、便利なので以下の2つをとりあえず、インストール。

Eclipse用のTomcatのプラグインをダウンロード&インストール

tomcatPluginV321.zipをダウンロードし、pluginsディレクトリーに放り込む。

プラグインをダウンロード&インストール

EclipseのHelp→Software Updatesでhttp://mevenide.codehaus.org/update/3.0/site.xmlを登録して、アップデートかければOK。

Struts1.3系をダウンロード

<URL:http://struts.apache.org/download.cgi>から、Struts 1.3.8をダウンロード。

今後のために、HibernateとXdocletとAntとMavenとHSQLDBをダウンロード。

※しっかりmavenを理解していると、上記のうちmaven以外は必要ないのですが、まだ理解できないところがいくつかあり、Antを利用したりしているので、とりあえず必要と言うことでご勘弁下さい。

上記の内、mavenとantは適当なところに解凍し、次に述べるようにパスなどを設定させます。また、その他のものは、可能なら、c:/java/に解凍し下さい(容量等で無理な場合は、今後場所を置き換えて考えてください)。

環境設定

基本的にはパスを通し、HOMEを指定する(Windowsでは「コントロールパネル」で行ってください)。

SET \
    PATH=%PATH%;C:\apache-ant-1.7.0\bin;C:\maven-2.0.7\bin;c:\jdk1.5.0_10\bin;C:\Tomcat \
    6.0\bin
set JAVA_HOME=C:\jdk1.5.0_10
set ANT_HOME = C:\apache-ant-1.7.0
set CATALINA_HOME = C:\Tomcat 6.0
set MAVEN_HOME = c:\maven-2.0.7

        

さあ、Hello World

  • プロジェクト名→JBBS
  • アプリケーション名→JBBS
  • パッケージ名→com.chikkun.jbbs

を前提に、とりあえず「Hello World」が出力されるプログラムを作成します。

mavenでプロジェクト作成

まずは、eclipseのworkspaceで

mvn archetype:create -DgroupId=com.chikkun.jbbs -Dversion=0.0.1 \
    -DartifactId=JBBS -DarchetypeArtifactId=maven-archetype-webapp
        

と打ち込み、次にmavenでeclipseのプロジェクトにもなるように修正を加える。作成したJBBSというディレクトリーにCDして

          maven eclipse:eclipse
        

と打ち込むと、今度はeclipse側から、既存のプロジェクトをワークスペースへインポートします。これで、eclipseでソースを編集しmavenでwarファイルにして、デプロイ(deploy)出来るようになります。

これで出来上がったディレクトリ・ファイルは次のようになります。

[JBBS]
 ├ [src]
 │  └ [main]
 │      ├ [resources]
 │      └ [webapp]
 │          ├ [WEB-INF]
 │          │  └ web.xml
 │          └ index.jsp
 ├ [target]
 │  ├ [classes]
 │  └ mvn-eclipse-cache.properties
 ├ .classpath
 ├ .project
 └ pom.xml
        

ここで、今後必要になり(自動で作成してくれない)ディレクトリーを作成し、

[JBBS]
 ├ [conf]
 ├ [src]
 │  ├ [main]
 │  │  ├ [java]
 │  │  │  └ [com]
 │  │  │      └ [chikkun]
 │  │  │          └ [jbbs]
 │  │  ├ [resources]
 │  │  └ [webapp]
 │  │      ├ [WEB-INF]
 │  │      │  └ [template]
 │  │      │     └ index.vm
 │  │      └ index.jsp
 │  └ [merge]
 ├ [target]
 │  └ [classes]
 ├ .classpath
 ├ .project
 └ pom.xml
        

のようにします。ポイントは、src/mainの下に、Javaのソースディレクトリーであるjavaを作成し、その下にcom.chikkun.jbbsというパッケージを作成するので、結果src/main/java/com/chikkun/jbbsを作成することと、web.xmlはあとでxdocletに作成させるので削除。また、src/main/webapp/WEB-INF/templateというディレクトリーを作成し、その中にsrc/main/webapp/index.jspをindex.vmというファイル名にして、コピーして下さい。さらに、src/mergeというディレクトリーを作成します。

できたpom.xmlを変更

できた、pom.xmlは以下のようです(\で改行されているのは、実際には改行されていない)。

<project xmlns="http://maven.apache.org/POM/4.0.0" \
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 \
    http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.chikkun.jbbs</groupId>
  <artifactId>JBBS</artifactId>
  <packaging>war</packaging>
  <version>0.0.1</version>
  <name>JBBS Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>JBBS</finalName>
  </build>
</project>

        

この辺から、段々ハマリ始めたので、文章にそれがにじみ出て来るかもしれません(つまり、内容が混乱しているかも)。

さて、strutsの1.3系を使いたいのですが、何故かリモート・リポジトリーにこれがありません。デフォルトのリポジトリー(たぶん)には、2系統も1.3系統もありません。

それですったもんだしたあげくに、ここを発見。ここもリポジトリーとして追加するために、pom.xmlを以下のように追加(リモート・リポジトリー先の追加と、dependencyとして、servlet-apiとstruts-coreなどを追加)。

<project xmlns="http://maven.apache.org/POM/4.0.0" \
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 \
    http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.chikkun.jbbs</groupId>
  <artifactId>JBBS</artifactId>
  <packaging>war</packaging>
  <version>0.0.1</version>
  <name>JBBS Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <repositories>
    <repository>
      <id>apache.snapshots</id>
      <name>ASF Maven 2 Snapshot</name>
      <url>http://people.apache.org/repo/m2-snapshot-repository</url>
    </repository>
  </repositories>
  
  <dependencies>
    <dependency>
      <groupId>org.apache.struts</groupId>
      <artifactId>struts-core</artifactId>
      <version>1.3.10-SNAPSHOT</version>
    </dependency>

    <dependency>
      <groupId>servletapi</groupId>
      <artifactId>servletapi</artifactId>
      <version>2.4</version>
    </dependency>

    <dependency>
      <groupId>velocity</groupId>
      <artifactId>velocity</artifactId>
      <version>1.5</version>
    </dependency>

    <dependency>
      <groupId>velocity-tools</groupId>
      <artifactId>velocity-tools</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>velocity-tools</groupId>
      <artifactId>velocity-tools-view</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>hsqldb</groupId>
      <artifactId>hsqldb</artifactId>
      <version>1.8.0.7</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>JBBS</finalName>
  </build>
</project>
</project>

        

そこで、DOSプロンプトから

        mvn package  
        

を実行すると、色々依存関係のあるjarファイルなどをダウンロードしますが、最終的に

          workspace/JBBS/target/JBBS.war
        

が出来ていれば、とりあえず成功です!(deployしてもまだ何も動かないけど)

JavaはXMLが一杯必要

Javaのサーブレットでは、web.xmlが必要ですし、strutsを使うにはさらにstruts-config.xmlが必要ですし、それ以外にはvalidateにも・・・などととにかくXMLが色々必要になります。これが結構面倒。とりあえずは、web.xmlとstruts-config.xmlが必要になるのですが、これを直接書かない方法で作成してみます。

そこで、Xdocletというユーティリティーを使い、ある程度自動で作成してくれるようにする。しか〜し、mavenでxdocletのプラグインの使い方がよくわからん。antのタスクとしてだとだいたいはわかるのと、結局色々調べるとmaven pluginは内部的にantタスクを利用しているらしいので、mavenからantを実行して、そのantでxdocletを動かすことにしました。

ソースから作成しないもの

xdocletは基本的には、xmlに色々書くのが面倒なので、ちょっとでも簡単にしようというものですが、ソースとは直接関係ないものは設定ファイルとして書き出すことが出来ません。したがって、それをmergeディレクトリーに置いておいて、それを使って、設定xmlを作成することになります。したがって、次のファイルをsrc/mergeディレクトリーに置いておいて下さい。

  • mime-mappings.xml

        <mime-mapping>
            <extension>csv</extension>
            <mime-type>application/x-excel</mime-type>
        </mime-mapping>
    
                  
    

    CSVのダウンロードなどの際、エクセルが立ち上がるように設定。

  • servlet-mappings.xml

    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>/action/*</url-pattern>
    </servlet-mapping>
    
      <servlet-mapping>
        <servlet-name>velocity</servlet-name>
        <url-pattern>*.vm</url-pattern>
      </servlet-mapping>
    
    <session-config>
      <session-timeout>
        30
     </session-timeout>
    </session-config>
    
            
                  
    

    action以下のURLだとstrutsのサーブレットとして指定し、vmという拡張子ではvelocityが処理し、sessionのタイムアウトは30分にする。

  • servlets.xml

        <servlet>
            <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
            <init-param>
                <param-name>config</param-name>
                <param-value>/WEB-INF/struts-config.xml</param-value>
            </init-param>
            <load-on-startup>2</load-on-startup>
        </servlet>
    
      <servlet>
        <servlet-name>velocity</servlet-name>
    <servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class>
        <init-param>
          <param-name>org.apache.velocity.toolbox</param-name>
          <param-value>/WEB-INF/toolbox.xml</param-value>
        </init-param>
        <init-param>
          <param-name>org.apache.velocity.properties</param-name>
          <param-value>/WEB-INF/velocity.properties</param-value>
        </init-param>
        <load-on-startup>10</load-on-startup>
      </servlet>
    
                  
    

    前項で指定した、actionやvelocityというサーブレットの実際の定義。

  • struts-data-sources.xml

    <data-sources>
    <data-source type="org.apache.commons.dbcp.BasicDataSource">
            <set-property
              property="driverClassName"
              value="org.hsqldb.jdbcDriver" />
    <set-property property="url" value="jdbc:hsqldb:file:WEB-INF/database/bbs" \
        />
            <set-property property="username" value="sa" />
            <set-property property="password" value="" />
    </data-source>
    </data-sources>
    
                  
    

    まだ使わないが、そのうち使うpure JavaなDBの設定。

  • struts-message-resources.xml
    <message-resources parameter="Resources.ApplicationResources"/>
    
    これもまだ使わないが、message-resourcesのファイル名。
                  
    
  • struts-plugins.xml

    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    <set-property property="pathnames" \
        value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" />
    </plug-in>
    
                  
    

    これまたまだ使わないが、validateするときに必要なもの設定(この設定により、上記のvalidator-rules.xmlが必要になる。中身はともかく必要なのでvalidator-rules.xmlを、src/main/webapp/WEB-INFに置いておいてください。。

  • web-settings.xml

    <display-name>Simple Java BBS</display-name>
    <description>
      Simple Java BBS by Chikkun and UndMeeR Guys
    </description>
    
                  
    

    単なる、このプロジェクトのdescription。

  • welcomefiles.xml

      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    
                  
    

    welcomeファイルの設定。

Eclipseのクラスパス

mavenで「eclipse:eclipse」して、Eclipseでインポートしただけだとまだ使えません(これで時々はまる)。「eclipse:eclipse」したあとに、pom.xmlのdependencyで他のライブラリーを追加すると、eclipseのクラスパスに自動で追加されるわけじゃないので、注意が必要。

つまり、pom.xmlに必要なライブラリーを追加すると、必ず「mvn eclipse:eclipse」を再度実行し、さらに、eclipse側のプロジェクトのプロパティー→Java Build Path→Order and Exportのチェックマークが外れていることがあるので、必ず確認した方が無難です。

ActionとActionFormの作成

前項の確認後、

 [src]
  └ [main]
     └ [java]
       └ [com]
           └ [chikkun]
               └ [jbbs]
                   ├ InputAction.java
                   └ TestActionForm.java  
          

というファイルを作成する。つまり、com.chikkun.jbbsというパッケージにInputActionとTestActionFormという2つのクラスを作成します。

  • InputAction
    package com.chikkun.jbbs;
    import java.io.IOException;
    import java.util.Date;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.struts.action.Action;
    import org.apache.struts.action.ActionForm;
    import org.apache.struts.action.ActionForward;
    import org.apache.struts.action.ActionMapping;
    import org.apache.struts.action.DynaActionForm;
    
    
    /**
     * @struts.action
     *      name="TestForm"
     *       path="/TestInput"
     * @struts.action-forward name="success" path="/WEB-INF/template/index.vm"
     * 
     * @author chikkun
     */
    public class InputAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm actionForm,
                HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
            //do nothing!
            return (mapping.findForward("success"));
        }
    }
    
                  
    
  • TestActionForm
    package com.chikkun.jbbs;
    import org.apache.struts.action.ActionForm;
    /**
     * @author chikkun
     * 
     * @struts.form name="TestForm"
     */
    public class TestActionForm extends ActionForm{
            private String name;
    
            public String getName() {
                    return name;
            }
    
            public void setName(String name) {
                    this.name = name;
            }
    }
    
                  
    

ここで重要なのは、コメント内の@マークの付いたもの。例えば、

/**
 * @struts.action
 *      name="TestForm"
 *       path="/TestInput"
 * @struts.action-forward name="success" path="/WEB-INF/template/index.vm"
 */
            

では、struts-config.xml内のactionの設定を記述していて、そして、xdocletを実行すると、それらが書き出されます。

xdocletを動くようにする

前述したように、mavenのxdocletプラグインを利用せず、AntプラグインでAntを実行し、Antでxdocletタスクを実行することにしました。ただ、本当だったらxdocletのライブラリーなどは共有したいところなんですが、よくわからないので、とりあえず、別途用意し、それを利用することにしました(無駄なんですがねえ!)。

  • build.propertiesをプロジェクトのルートディレクトリーに作成。内容は以下。
    DATAFILE_PREFIX=file:WEB-INF/database/bbs
    CATALINA_HOME=c:/Tomcat 6.0
    ARCHIVE_DIR=c:/java
    HIBERNATE_HOME=${ARCHIVE_DIR}/hibernate-3.2
    XDOCLET_HOME=${ARCHIVE_DIR}/xdoclet-1.2.3
    HSQLDB_HOME=${ARCHIVE_DIR}/hsqldb
    STRUTS_HOME=${ARCHIVE_DIR}/struts-1.2.7
    ROOT=c:/Tomcat 6.0/webapps/jbbs/
    PROJECT=webapps/jbbs/
    
  • pom.xmlの下の方にあるbuildタグを下のように追加修正。

      <build>
        <plugins>
          <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
              <execution>
                <!-- id タグは executions 内に複数の execution タグを含める場合に必要. -->
                <id>web_xml</id>
                <phase>compile</phase>
                <configuration>
                  <tasks>
                    <!-- ここに Ant のタスクを記述する. -->
                    <ant antfile="build.xml" target="web" inheritRefs="true" />
                  </tasks>
                </configuration>
                <goals>
                  <goal>run</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
        </plugins>
        <finalName>JBBS</finalName>
      </build>
    

    これで、「mvn package」などを実行した際、ソースをコンパイされるときにantが実行されるようになります。

  • build.xml(antが利用)
    <?xml version="1.0" encoding="UTF-8"?>
    
    <project name="jbbs" default="web">
    
      <!-- define properties. -->
      <property file="./build.properties" />
      <property name="src.dir" value="./src/main/java" />
      <property name="merge.dir" value="./src/merge" />
      <property name="classes.dir" value="./target/JBBS/classes" />
      <property name="local.encoding" value="Windows-31J"/>
      <property name="target.web_inf" value="./target/JBBS/WEB-INF"/>
      <property name="src.web_inf" value="./src/main/webapp/WEB-INF"/>
    
      <!-- define library filesets. -->
      <fileset id="struts.libs" dir="${STRUTS_HOME}/lib">
        <include name="*.jar"/>
      </fileset>
    
      <fileset id="xdoclet.libs" dir="${XDOCLET_HOME}/lib">
        <include name="*.jar"/>
      </fileset>
      <fileset id="servlet.jar" file="${CATALINA_HOME}/lib/servlet-api.jar" />
    
      <!-- define build classpath. -->
      <path id="build.classpath">
        <pathelement path="${classes.dir}" />
        <fileset refid="struts.libs" />
        <fileset refid="xdoclet.libs" />
        <fileset refid="servlet.jar" />
      </path>
    
      <!-- define targets. -->
      <target name="struts">
            <!-- XDocletタスクを使用する設定 -->
            <taskdef
                name="webdoclet"
                classname="xdoclet.modules.web.WebDocletTask"
                classpathref="build.classpath"/>
            <!-- XDocletの実行 -->
            <webdoclet
                destdir="${src.dir}"
                mergedir="${merge.dir}"
                excludedtags="@version,@author,@todo,@param"
                force="true"
                verbose="true"
                encoding="${local.encoding}">
                <!-- 対象となるソースファイルの指定 -->
                <fileset dir="${src.dir}">
                    <include name="**/*.java"/>
                </fileset>
                <!-- struts-config.xmlの生成 -->
                <strutsconfigxml version="1.2" destdir="${src.web_inf}"/>
                <!-- validation.xmlの生成 -->
                <strutsvalidationxml destdir="${src.web_inf}"/>
            </webdoclet>
        </target>
    
        <target name="web" depends="struts">
            <!-- XDocletタスクの宣言 -->
            <taskdef
                name="webdoclet"
                classname="xdoclet.modules.web.WebDocletTask"
                classpathref="build.classpath"
                />
            <!-- XDocletの実行 -->
            <webdoclet
                destdir="${src.dir}"
                mergedir="${merge.dir}"
                excludedtags="@version,@author,@todo"
                force="true"
                verbose="false"
                encoding="${local.encoding}">
                <!-- 対象となるソースファイルの指定 -->
                <fileset dir="${src.dir}">
                    <include name="**/*.java"/>
                </fileset>
                <!-- デプロイメントディスクリプタ(web.xml)の生成 -->
                <deploymentdescriptor servletspec="2.4" 
                                      destdir="${src.web_inf}"/>
            </webdoclet>
    
            <fixcrlf srcdir="${src.web_inf}"
               cr="remove"
               includes="*.xml"
               excludes="tiles-defs.xml,holidays.xml,equipment.xml"
            />
        </target>
    </project>
    

mvn package

さて、これで準備が整ったので、プロジェクトのルートディレクトリーで「mvn package」を実行します。問題がなければ、ソースはコンパイルされ、web.xml等は書き出され、warファイルがtargetディレクトリーに作成されます。これをtomcatのwebappsに置いて、「http://localhost:8080/JBBS/action/TestInput」に悪制すして、「Hello World!」とブラウザーに出力されれば、成功です。ふう。

HibernateでHSQLDBをちょっぴり触ってみる

Hibernateを導入する方法として、大まかに言うと次の2つの方向が考えられます。

  • テーブルに対応するDTO(Data Transfer Object)を作成し、そこにxdocletのタグを書いておきstrutsの場合と同様必要な hbm ファイル(DBのマッピングファイル---何の省略だろう?)を作成してもらったり、DBのSchema(SQL文)を作成してもらうという方法。これはhibernate-toolsを利用します。
  • もう1つの方法は、すでにDBにテーブルがある場合で、このDBの内容から、DTOとhbmを作成しようという方法。これはMiddlegenを使ってhbmファイル作成し、齟齬のそのhbmファイルからhibernate-toolsでDTOを作成することになります。

以前は、1つめの方法を使っていたが、今回はすでにテーブルがあるので、2番目の方法で行こうと思います。

Middlegenを使って、DTOなどを自動で作成してしまおう

Middegenのインストール

  • Middlen から、Middlegen 2.1をダウンロード、c:/javaに解凍。
  • build.propertiesに次の文言を追加。
                MIDDLEGEN_HOME=${ARCHIVE_DIR}/middlegen-2.1
              
    
  • Hibernate Tools から、Hibernate Toolsをダウンロード、c:/javaに解凍。
  • build.propertiesに次の文言を追加。
                HIBERNATE_TOOLS_HOME=${ARCHIVE_DIR}/HibernateTools-3.2.0.beta9a
              
    
  • build.xmlに

      <fileset id="hibernate-tools.libs" dir="${HIBERNATE_TOOLS_HOME}">
        <include name="*.jar"/>
      </fileset>
      <fileset id="middlegen.libs" dir="${MIDDLEGEN_HOME}">
        <include name="*.jar"/>
        <include name="samples/lib/*.jar"/>
      </fileset>
    
              
    

    を加え、build.classpathにも次のように加える。

      <path id="build.classpath">
        <pathelement path="${classes.dir}" />
        <fileset refid="struts.libs" />
        <fileset refid="xdoclet.libs" />
        <fileset refid="servlet.jar" />
        <fileset refid="middlegen.libs"/>
        <fileset refid="hibernate-tools.libs"/>
      </path>
    

DBの設定

最初はPure JavaなHSQLDBを利用しようと思っていたが、PostgreSQLがすでに動いていたので(URLは//localhost:5432/dum_bbs)、こちらで行くことにしました。

  • pom.xmlに以下を加える。
        <dependency>
          <groupId>postgresql</groupId>
          <artifactId>postgresql</artifactId>
          <version>8.1-408.jdbc3</version>
        </dependency>
    
                  
    

build.xml

Middlegenを使って、書き換えるタスクを書き加えます。

    <!--これは、hibernate用のbeanを書き換えてしまうので、注意を要する。-->
    <target name="middlegen" description="Databaseのtableからhbmファイルとbeanを作成">
<taskdef name="middlegen" classname="middlegen.MiddlegenTask" \
    classpathref="build.classpath"/>
      <!--schema="publicがあったら、なぜかだめ"-->
<middlegen appname="jbbs" prefsdir="src/main/webapp/WEB-INF/middlegendir" \
    gui="false"
driver="org.postgresql.Driver" \
    databaseurl="jdbc:postgresql://localhost:5432/dum_bbs"
datasourceJNDIName="" username="chikkun" password="kazukun" catalog=""
                 initialContextFactory="${java.naming.factory.initial}"
                 providerURL="${java.naming.provider.url}">
<hibernate destination="${target.web_inf}/classes" genXDocletTags="false"
                   package="com.chikkun.jbbs.database"
javaTypeMapper="middlegen.plugins.hibernate.HibernateJavaTypeMapper"/>
      </middlegen>
<taskdef name="hbm2java" \
    classname="net.sf.hibernate.tool.hbm2java.Hbm2JavaTask"
               classpathref="build.classpath"/>
    <hbm2java output="src/main/java">
      <fileset dir="${classes.dir}">
        <include name="**/*.hbm.xml"/>
      </fileset>
    </hbm2java>

    </target>

いざ実行

とりあえず、上記(build.xml)のmiddlegenタスク実行してみる。

ant middlegen
          

たまに、DBへのアクセスで失敗するときがあったけど、2度目か最悪3度目にはOKでした。

成功すると、次のようなファイルができている。

[java]
 └ [com]
     └ [chikkun]
         └ [jbbs]
             ├ [database]
             │  ├ Bb.java
             │  ├ SqlFeature.java
             │  ├ SqlImplementationInfo.java
             │  ├ SqlLanguage.java
             │  ├ SqlPackage.java
             │  ├ SqlSizing.java
             │  ├ SqlSizingProfile.java
             │  └ User.java
             ├ InputAction.java
             └ TestActionForm.java


        

何故かbbsテーブルのDTOがBb.javaになってしまっているのかは不明。上記のDTOはBb.javaとUser.javaで、hbm.xmlで終わっているものがマッピングファイルです。Bbテーブル(本来はBbs.javaになるはず?)を例にとって説明すると、DTO自体はPOJO(Plain Old java Object---つまり普通のクラス)で、ただのデータビーンです。このフィールドが、テーブルのどれに対応しているのか、というマッピングの内容がBb.hbm.xmlに書かれているわけです。

package com.chikkun.jbbs.database;

import java.io.Serializable;
import java.util.Date;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

/** @author Hibernate CodeGenerator */
public class Bb implements Serializable {

    /** identifier field */
    private Integer id;

    /** persistent field */
    private int parent;

    /** persistent field */
    private String title;

    /** persistent field */
    private String contents;

    /** persistent field */
    private Date registDate;

    /** nullable persistent field */
    private String userId;

    /** nullable persistent field */
    private String url;

    /** nullable persistent field */
    private String EMail;

    /** nullable persistent field */
    private Integer deleteFlag;

    /** full constructor */
public Bb(Integer id, int parent, String title, String contents, Date \
    registDate, String userId, String url, String EMail, Integer \
    deleteFlag) {
        this.id = id;
        this.parent = parent;
        this.title = title;
        this.contents = contents;
        this.registDate = registDate;
        this.userId = userId;
        this.url = url;
        this.EMail = EMail;
        this.deleteFlag = deleteFlag;
    }

    /** default constructor */
    public Bb() {
    }

    /** minimal constructor */
public Bb(Integer id, int parent, String title, String contents, Date \
    registDate) {
        this.id = id;
        this.parent = parent;
        this.title = title;
        this.contents = contents;
        this.registDate = registDate;
    }

    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public int getParent() {
        return this.parent;
    }

    public void setParent(int parent) {
        this.parent = parent;
    }

    public String getTitle() {
        return this.title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContents() {
        return this.contents;
    }

    public void setContents(String contents) {
        this.contents = contents;
    }

    public Date getRegistDate() {
        return this.registDate;
    }

    public void setRegistDate(Date registDate) {
        this.registDate = registDate;
    }

    public String getUserId() {
        return this.userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUrl() {
        return this.url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getEMail() {
        return this.EMail;
    }

    public void setEMail(String EMail) {
        this.EMail = EMail;
    }

    public Integer getDeleteFlag() {
        return this.deleteFlag;
    }

    public void setDeleteFlag(Integer deleteFlag) {
        this.deleteFlag = deleteFlag;
    }

    public String toString() {
        return new ToStringBuilder(this)
            .append("id", getId())
            .toString();
    }

    public boolean equals(Object other) {
        if ( !(other instanceof Bb) ) return false;
        Bb castOther = (Bb) other;
        return new EqualsBuilder()
            .append(this.getId(), castOther.getId())
            .isEquals();
    }

    public int hashCode() {
        return new HashCodeBuilder()
            .append(getId())
            .toHashCode();
    }

}

マッピングファイルは(Bb.hbm.xml)は以下のようになります。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
    
<hibernate-mapping>
<!-- 
    Created by the Middlegen Hibernate plugin 2.1

    http://boss.bekk.no/boss/middlegen/
    http://www.hibernate.org/
-->

<class 
    name="com.chikkun.jbbs.database.Bb" 
    table="bbs"
>

    <id
        name="id"
        type="java.lang.Integer"
        column="id"
    >
        <generator class="assigned" />
    </id>

    <property
        name="parent"
        type="int"
        column="parent"
        not-null="true"
        length="4"
    />
    <property
        name="title"
        type="java.lang.String"
        column="title"
        not-null="true"
        length="256"
    />
    <property
        name="contents"
        type="java.lang.String"
        column="contents"
        not-null="true"
        length="-1"
    />
    <property
        name="registDate"
        type="java.sql.Date"
        column="regist_date"
        not-null="true"
        length="4"
    />
    <property
        name="userId"
        type="java.lang.String"
        column="user_id"
        length="256"
    />
    <property
        name="url"
        type="java.lang.String"
        column="url"
        length="512"
    />
    <property
        name="EMail"
        type="java.lang.String"
        column="e_mail"
        length="512"
    />
    <property
        name="deleteFlag"
        type="java.lang.Integer"
        column="delete_flag"
        length="4"
    />

    <!-- Associations -->
  

</class>
</hibernate-mapping>

とりあえず、親子関係のあるテーブルじゃないので、だいたい読めばわかると思います。例えば、

<class 
    name="com.chikkun.jbbs.database.Bb" 
    table="bbs"
>

    <id
        name="id"
        type="java.lang.Integer"
        column="id"
    >
        <generator class="assigned" />
    </id>

      

ですと、Javaの「com.chikkun.jbbs.database.Bb」クラスとテーブルのbbsが対応していて、フィールドに関しては、主キーはテーブルのidでそれはDTOのidに対応していて、さらにIntegerクラスの型だということになります。最後のgeneratorでのassignedは主キーの生成を自分で作成するという指定になります(これなどは、面倒なのであとで修正する必要があるでしょう)。

Hibernate.cfg.xmlを手作業で

さすがにこれまでは作ってくれないので、作成します。下記を記入し、src/main/webapp/WEB-INF/resources(最終的にはtarget/classesになるはず)に保存してください。

<?xml version="1.0"?>
<!DOCTYPE hibernate-configuration PUBLIC
  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  "hibernate-configuration-3.0.dtd">

<hibernate-configuration>
  <session-factory>
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
    <property name="show_sql">false</property>
<property name="connection.driver_class">org.postgresql.Driver</property>
<property \
    name="connection.url">jdbc:postgresql://localhost:5432/dum_bbs</property>
    <property name="connection.username">chikkun</property>
    <property name="connection.password">kazukun</property>
    <mapping resource="com/chikkun/jbbs/database/Bb.hbm.xml"/>
    <mapping resource="com/chikkun/jbbs/database/User.hbm.xml"/>
  </session-factory>
</hibernate-configuration>

DAO(Data Transfer Object)の作成

とりあえず、単純なCRUDを備えたものを作成。BaseDAOを作成し、BsDAOがそれをextendsするようにする。

  • BaseDAO
    package com.chikkun.jbbs.database;
    
    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    
    /**
     * DAOベースクラス
     */
    public class BaseDAO {
    
        /** セッションファクトリ */
        private static SessionFactory sessionFactory = null;
    
        /**
         * セッションファクトリを取得します。 最初に呼び出されたときに、初期化処理を行います。
         */
        static synchronized SessionFactory getSessionFactory()
                throws HibernateException {
            if (sessionFactory == null) {
                Configuration cfg = new Configuration();
                cfg.configure();
                sessionFactory = cfg.buildSessionFactory();
            }
            return sessionFactory;
        }
    
        /**
         * セッションを取得します。
         */
        public Session getSession() throws HibernateException {
            SessionFactory sf = getSessionFactory();
            return sf.openSession();
        }
    
    }
    
  • BbDAO
    package com.chikkun.jbbs.database;
    
    import java.util.List;
    
    import org.hibernate.Criteria;
    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    import org.hibernate.criterion.Expression;
    
    /**
     * @author chikkun
     */
    public class BbDAO extends BaseDAO {
    
        public void delete(Bb bb_) {
            Session session = null;
            Transaction transaction = null;
            try {
                session = getSession();
                transaction = session.beginTransaction();
                session.delete(bb_);
                transaction.commit();
            } catch (HibernateException ex) {
                //logger.info("データベースエラー:" + ex.getMessage());
                if (transaction != null) {
                    try {
                        transaction.rollback();
                    } catch (HibernateException ignore) {
                    }
                }
                throw new RuntimeException(ex.getMessage());
            } finally {
                if (session != null) {
                    try {
                        session.close();
                    } catch (HibernateException ignore) {
                    }
                }
            }
        }
    
        public void save(Bb bb_) {
            Session session = null;
            Transaction transaction = null;
            try {
                session = getSession();
                transaction = session.beginTransaction();
    
                session.save(bb_);
                transaction.commit();
            } catch (HibernateException ex) {
                //logger.info("データベースエラー:" + ex.getMessage());
                if (transaction != null) {
                    try {
                        transaction.rollback();
                    } catch (HibernateException ignore) {
                    }
                }
                throw new RuntimeException(ex.getMessage());
            } finally {
                if (session != null) {
                    try {
                        session.close();
                    } catch (HibernateException ignore) {
                    }
                }
            }
        }
    
        public void saveOrUpdate(Bb bb_) {
            Session session = null;
            Transaction transaction = null;
            try {
                session = getSession();
                transaction = session.beginTransaction();
    
                session.saveOrUpdate(bb_);
                transaction.commit();
            } catch (HibernateException ex) {
                //logger.info("データベースエラー:" + ex.getMessage());
                if (transaction != null) {
                    try {
                        transaction.rollback();
                    } catch (HibernateException ignore) {
                    }
                }
                throw new RuntimeException(ex.getMessage());
            } finally {
                if (session != null) {
                    try {
                        session.close();
                    } catch (HibernateException ignore) {
                    }
                }
            }
        }
    
        public void update(Bb bb_) {
            Session session = null;
            Transaction transaction = null;
            try {
                session = getSession();
                transaction = session.beginTransaction();
                session.update(bb_);
                transaction.commit();
            } catch (HibernateException ex) {
                //logger.info("データベースエラー:" + ex.getMessage());
                if (transaction != null) {
                    try {
                        transaction.rollback();
                    } catch (HibernateException ignore) {
                    }
                }
                throw new RuntimeException(ex.getMessage());
            } finally {
                if (session != null) {
                    try {
                        session.close();
                    } catch (HibernateException ignore) {
                    }
                }
            }
        }
    
    
        public List findById(Integer id) {
            List result = null;
            Session session = null;
            try {
                session = getSession();
                Criteria criteria = session.createCriteria(Bb.class);
                if (id != null) {
                    criteria.add(Expression.eq("id", id));
                }
                result = criteria.list();
            } catch (HibernateException ex) {
                //logger.info("データベースエラー:" + ex.getMessage());
                throw new RuntimeException(ex.getMessage());
            } finally {
                if (session != null) {
                    try {
                        session.close();
                    } catch (HibernateException ignore) {
                    }
                }
            }
            return result;
        }
    
        public List findAll() {
            List result;
            Session session = null;
            try {
                session = getSession();
                Criteria criteria = session.createCriteria(Bb.class);
    
                result = criteria.list();
            } catch (HibernateException ex) {
                //logger.info("データベースエラー:" + ex.getMessage());
                throw new RuntimeException(ex.getMessage());
            } finally {
                if (session != null) {
                    try {
                        session.close();
                    } catch (HibernateException ignore) {
                    }
                }
            }
            return result;
        }
    }
    

ActionクラスとActionFormクラスにちょっと追加

  • package com.chikkun.jbbs;
    import java.io.IOException;
    import java.util.List;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.struts.action.Action;
    import org.apache.struts.action.ActionForm;
    import org.apache.struts.action.ActionForward;
    import org.apache.struts.action.ActionMapping;
    
    import com.chikkun.jbbs.database.BbDAO;
    
    
    /**
     * @struts.action
     *      name="TestForm"
     *       path="/TestInput"
     * @struts.action-forward name="success" path="/WEB-INF/template/index.vm"
     * 
     * @author chikkun
     * @version $Id: InputAction.java 366021 2006-01-04 23:14:07Z ltheussl $ 
     */
    public class InputAction extends Action {
        /**
         * Performs the simple action
         * 
         * @param mapping
    * the action mappings where you find the return value of the
         *                   forward
         * @param actionForm
    * the action form used, in this example it will be an instance
         *                   of the DynaActionForm class
         * @param request
         *                   the HTTP servlet request
         * @param response
         *                   the HTTP servlet response
         */
    public ActionForward execute(ActionMapping mapping, ActionForm actionForm,
                HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
            BbDAO bb = new BbDAO();
            List list = bb.findAll();
            TestActionForm form = (TestActionForm) actionForm;
            form.setList(list);
            return (mapping.findForward("success"));
        }
    }
    
                
    

    の、

            BbDAO bb = new BbDAO();
            List list = bb.findAll();
            TestActionForm form = (TestActionForm) actionForm;
            form.setList(list);
    
                
    

    ここがまさにHibernateを使っている場面。

  • ActionForm(フィールドとsetter・getterを加えただけ)
    package com.chikkun.jbbs;
    import java.util.List;
    
    import org.apache.struts.action.ActionForm;
    /**
     * @author chikkun
     * 
     * @struts.form name="TestForm"
     */
    public class TestActionForm extends ActionForm{
            private String name;
            private List list;
    
            public List getList() {
                    return list;
            }
    
            public void setList(List list) {
                    this.list = list;
            }
    
            public String getName() {
                    return name;
            }
    
            public void setName(String name) {
                    this.name = name;
            }
    }
    

vmをちょこっと修正

データベースから拾ったデータをテンプレートで表示するので、ほんのちょっとtemplate/index.vmを修正します(foreach文のところです。

<html>
<body>
<h2>Hello World! and Hibernate!!</h2>
<table>
<tr><th>title</th><th>user ID</th></tr>
#foreach($bean in $TestForm.getList())
<tr>
<td>$bean.getTitle()</td><td>$bean.getUserId()</td>
</tr>
#end

</table>
</body>
</html>

さあwarを作って確認

Hibernateのjarも必要になるので、build.properties内に

	  HIBERNATE_HOME=${ARCHIVE_DIR}/hibernate-3.2
	

があることを確認後、build.xmlに

  <fileset id="hibernate.libs" dir="${HIBERNATE_HOME}">
    <include name="*.jar"/>
    <include name="lib/*.jar"/>
  </fileset>
  <path id="build.classpath">
    <pathelement path="${classes.dir}" />
    <fileset refid="struts.libs" />
    <fileset refid="xdoclet.libs" />
    <fileset refid="servlet.jar" />
    <fileset refid="middlegen.libs"/>
    <fileset refid="hsqldb.libs"/>
    <fileset refid="hibernate-tools.libs"/>
    <fileset refid="dom4j.libs"/>
    <fileset refid="hibernate.libs"/>
  </path>

	

と追加します(antでコンパイルしなければ、必要ないですが)。次にpom.xml内に

    <dependency>
      <groupId>hibernate</groupId>
      <artifactId>hibernate</artifactId>
      <version>3.1rc2</version>
    </dependency>


	

とします。ついでにmvn eclipse:eclipseで.classpathを更新し、eclipse上でビルドパスにチェックマークを入れます。

とりあえず(もし、マッピングファイルを作成していなったらant middlegenも)

          mvn install
        

残念!文字化け。おう忘れておった。

encodingのfilterクラスを作成

src/main/java/com/chikkun/common/SetCharacterEncodingFilter.javaを作成。

package com.chikkun.common;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


/**
 * @author Craig McClanahan
 * @version $Revision: 1.1 $ $Date: 2005/03/31 11:45:44 $
 * @web.filter
 *    description="character encoding"
 *    name="Set Character Encoding"
 * @web.filter-init-param
 *    name="encoding"
 *    value="Windows-31J"
 * @web.filter-mapping
 *    url-pattern="/*"
 *    servlet-name="Set Character Encoding"
 */
public class SetCharacterEncodingFilter implements Filter {


// ----------------------------------------------------- Instance Variables


    /**
     * The default character encoding to set for requests that pass through
     * this filter.
     */
    protected String encoding = null;


    /**
* The filter configuration object we are associated with. If this value
     * is null, this filter instance is not currently configured.
     */
    protected FilterConfig filterConfig = null;


    /**
     * Should a character encoding specified by the client be ignored?
     */
    protected boolean ignore = true;


// --------------------------------------------------------- Public Methods


    /**
     * Take this filter out of service.
     */
    public void destroy() {

        this.encoding = null;
        this.filterConfig = null;

    }


    /**
     * Select and set (if specified) the character encoding to be used to
     * interpret request parameters for this request.
     *
     * @param request The servlet request we are processing
     * @param result The servlet response we are creating
     * @param chain The filter chain we are processing
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet error occurs
     */
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
        throws IOException, ServletException {

        // Conditionally select and set the character encoding to be used
        if (ignore || (request.getCharacterEncoding() == null)) {
            String encoding = selectEncoding(request);
            if (encoding != null)
                request.setCharacterEncoding(encoding);
        }

        // Pass control on to the next filter
        chain.doFilter(request, response);

    }


    /**
     * Place this filter into service.
     *
     * @param filterConfig The filter configuration object
     */
    public void init(FilterConfig filterConfig) throws ServletException {

        this.filterConfig = filterConfig;
        this.encoding = filterConfig.getInitParameter("encoding");
        String value = filterConfig.getInitParameter("ignore");
        if (value == null)
            this.ignore = true;
        else if (value.equalsIgnoreCase("true"))
            this.ignore = true;
        else if (value.equalsIgnoreCase("yes"))
            this.ignore = true;
        else
            this.ignore = false;

    }


// ------------------------------------------------------ Protected Methods


    /**
     * Select an appropriate character encoding to be used, based on the
     * characteristics of the current request and/or filter initialization
     * parameters.  If no character encoding should be set, return
     * <code>null</code>.
     * <p>
* The default implementation unconditionally returns the value configured
     * by the <strong>encoding</strong> initialization parameter for this
     * filter.
     *
     * @param request The servlet request we are processing
     */
    protected String selectEncoding(ServletRequest request) {

        return (this.encoding);

    }
}

一応これでrequestから来るデータなどは大丈夫だが、DBから取得したものを直接velocityに私、そこで参照する場合は無理。そこで

Velocityの設定など

velocity.propertiesなどを作成していなかったので、これをsrc/main/webapp/WEB-INFにそれぞれダウンロードして、置いてください。

これで、準備OKで再度warを作成し、deployするとOKです。もし、僕の記述がおかしくかったら、JBBS.warを解凍して、確認してください。

ちょっとYAML,strutsでvalidate

YAML

JavaではあまりYAMLは利用されていないようですが、jvyamlというのとjyamlという2つが一応あるようです。どちらも、最初以前使っていたbbs.ymlを読み込ませてもエラーになり、次の2つを行うとjvyamlは動いたので(次の2つを行ってjyamlは実験していない---先に動いた方を使う)、このjvyamlを使うことにした。

  • &nbspの&を削除
  • 日本語をunicode(native2asciiを使って)に変換

しかし、もう1つ問題が起こった。読み込んだ日本語がUnicodeのままで(「\u305f\u3051\u304a」のような)、これを元に戻せない。色々調べたが、native2asciiの\reverseオプションを使えばよいとか、普通にStringに入れた場合には、自動で変換されて日本語になるようなこともあったりですが、現実jvyamlからもらったものはunicodeでprintlnしても、やはりそのままであった。

そこで「はたと」思いついたのが、もともとJava標準のPropertiesというクラスでは、これを使って読み込むとしっかり日本語に戻っているので、このソースの中にそのヒントがあるんじゃないかと思いつき、中身を見たら、「バッチグー」。これをstaticなユーティリティークラスにすればOKです(Actionの中でメソッドとして使ってしっかり日本語に戻っているのを確かめました)。本当は、jvyamlの中に入れ込むのが一番なんだけど・・・。

    /*
     * Converts encoded &#92;uxxxx to unicode chars
     * and changes special saved chars to their original forms
     */
    private String loadConvert(String theString) {
        char aChar;
        int len = theString.length();
        StringBuffer outBuffer = new StringBuffer(len);

        for (int x=0; x<len; ) {
            aChar = theString.charAt(x++);
            if (aChar == '\\') {
                aChar = theString.charAt(x++);
                if (aChar == 'u') {
                    // Read the xxxx
                    int value=0;
                    for (int i=0; i<4; i++) {
                        aChar = theString.charAt(x++);
                        switch (aChar) {
                          case '0': case '1': case '2': case '3': case '4':
                          case '5': case '6': case '7': case '8': case '9':
                             value = (value << 4) + aChar - '0';
                             break;
                          case 'a': case 'b': case 'c':
                          case 'd': case 'e': case 'f':
                             value = (value << 4) + 10 + aChar - 'a';
                             break;
                          case 'A': case 'B': case 'C':
                          case 'D': case 'E': case 'F':
                             value = (value << 4) + 10 + aChar - 'A';
                             break;
                          default:
                              throw new IllegalArgumentException(
                                           "Malformed \\uxxxx encoding.");
                        }
                    }
                    outBuffer.append((char)value);
                } else {
                    if (aChar == 't') aChar = '\t';
                    else if (aChar == 'r') aChar = '\r';
                    else if (aChar == 'n') aChar = '\n';
                    else if (aChar == 'f') aChar = '\f';
                    outBuffer.append(aChar);
                }
            } else
                outBuffer.append(aChar);
        }
        return outBuffer.toString();
    }

Velocity-toolsのバージョンアップ

Velocity-toolsのバージョンが1.2のままなのが、ちょっと気になるので、1.3のmavenのリモートリポジトリーがないか、さんざん探しましたが、みつかりません(jyamlはいらないかもしれません---あとでまたまた変えるかもしれないのでとってありますが)。

というわけで、jarファイルを直接登録する方法を採用(YAMLのjvyamlもないことだし)。つまり、僕の場合は、c:/java以下にそれぞれのライブラリーをダウンロード、解凍しておいて、それを以下のコマンドを実行して直接登録、POMにも書き込みます。

  • mvn install:install-file -Dfile=C:\java\velocity-tools-1.3\lib\velocity-tools-1.3.jar -DgroupId=velocity-tools -DartifactId=velocity-tools -Dversion=1.3 -Dpackaging=jar -DgeneratePom=true
  • mvn install:install-file -Dfile=C:\java\velocity-tools-1.3\lib\velocity-tools-view-1.3.jar -DgroupId=velocity-tools -DartifactId=velocity-tools-view -Dversion=1.3 -Dpackaging=jar -DgeneratePom=true
  • mvn install:install-file -Dfile=C:\java\jyaml-1.2\jyaml-1.2.jar -DgroupId=jyaml -DartifactId=jyaml -Dversion=1.2 -Dpackaging=jar -DgeneratePom=true
  • mvn install:install-file -Dfile=C:\java\jvyaml-0.2.1\lib\jvyaml.jar -DgroupId=jvyaml -DartifactId=jvyaml -Dversion=0.2.1 -Dpackaging=jar -DgeneratePom=true
  • Hibernateのlibディレクトリーにある物でもOK。

    mvn install:install-file -Dfile=C:\java\jta-1.0.1B.jar -DgroupId=javax.transaction -DartifactId=jta -Dversion=1.0.1B -Dpackaging=jar -DgeneratePom=true

  • Acegiのセキュリティもいずれいるし、最新のはrepositoryにないので

    mvn install:install-file -Dfile=C:\java\acegi-security-1.0.4\acegi-security-1.0.4.jar -DgroupId=acegi -DartifactId=acegi -Dversion=1.0.4 -Dpackaging=jar -DgeneratePom=true

    <dependency>
      <groupId>jyaml</groupId>
      <artifactId>jyaml</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>jvyaml</groupId>
      <artifactId>jvyaml</artifactId>
      <version>0.2.1</version>
    </dependency>
    <dependency>
      <groupId>velocity-tools</groupId>
      <artifactId>velocity-tools</artifactId>
      <version>1.3</version>
    </dependency>

    <dependency>
      <groupId>velocity-tools</groupId>
      <artifactId>velocity-tools-view</artifactId>
      <version>1.3</version>
    </dependency>


        

上記をpom.xmlに書き込み、忘れずに、toolbox.xmlなどを併せて新しくする(アーカイブに付いてくるexampleのwarの中にある)。

Xdocletがstruts1.3に対応していない件

xdocletのwebdocletでstrutsが1.2までしか対応していない件が、ちょっと気になったので、最新のCVSからダウンロードしてものなどで実験してみたが、やはり対応していない。なので、ソースをいじって無理無理対応してみた。

xdoclet/modules/apache/src/xdoclet/modules/apache/struts/StrutsConfigXmlSubTask.java
          

というのが、strutsタグを担当しているようなので、このなかを1.2となんかと同様に1.3の部分を追加。さらに

xdoclet/modules/apache/src/xdoclet/modules/apache/struts/resources/struts-config_1_3.dtd
          

をstrutsのアーカイブから持ってきて、保存する。そして、antを実行して、jarを作成した。

修正は簡単ですが、面倒だったらxdocletの再コンパイルしたものをダウンロードし、そして、C:\java\xdoclet-1.2.3\libあたりに展開してください。

hbmファイルを作る方法2

最初は実際存在するDBからhbmファイルやDTOを作成しましたが、今後の小さな修正は反対方向、つまりDTOからhbmファイル、スキマー(SQL)を作成するという方向性に変更します。

つまり、DTOにXdocletのタグを書いていき、その後は自動で、hbmファイルを作成しようというわけです。そのためにはgetterにXdocletのタグと、Antのbuild.xmlに追加が必要です(ついでだから、Eclipseのリファクタリング機能を使って、Bb.javaをBBS.javaに変更)。

  • BBS.java
    package com.chikkun.jbbs.database;
    
    import java.io.Serializable;
    import java.util.Date;
    import org.apache.commons.lang.builder.EqualsBuilder;
    import org.apache.commons.lang.builder.HashCodeBuilder;
    import org.apache.commons.lang.builder.ToStringBuilder;
    
    /**
     * @author chikkun
     * @hibernate.class
     *   table="BBS"
     */
    public class BBS implements Serializable {
    
        /** identifier field */
        private Integer id;
    
        /** persistent field */
        private int parent;
    
        /** persistent field */
        private String title;
    
        /** persistent field */
        private String contents;
    
        /** persistent field */
        private Date registDate;
    
        /** nullable persistent field */
        private String userId;
    
        /** nullable persistent field */
        private String url;
    
        /** nullable persistent field */
        private String email;
    
        /** nullable persistent field */
        private Integer deleteFlag;
    
        /** full constructor */
        public BBS(Integer id, int parent, String title, String contents,
                Date registDate, String userId, String url, String EMail,
                Integer deleteFlag) {
            this.id = id;
            this.parent = parent;
            this.title = title;
            this.contents = contents;
            this.registDate = registDate;
            this.userId = userId;
            this.url = url;
            this.email = EMail;
            this.deleteFlag = deleteFlag;
        }
    
        /** default constructor */
        public BBS() {
        }
    
        /** minimal constructor */
        public BBS(Integer id, int parent, String title, String contents,
                Date registDate) {
            this.id = id;
            this.parent = parent;
            this.title = title;
            this.contents = contents;
            this.registDate = registDate;
        }
    
        /**
         *  @hibernate.id
         *  column="ID"
         *  unsaved-value="null"             
         *  generator-class="native"
         * @hibernate.generator-param
         *    name="sequence"
         *    value="SEQ_BBS"
         * 
         * @return Returns the id.
         */
        public Integer getId() {
            return this.id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        /**
         * @hibernate.property
         * column="PARENT"
         * not-null="true"
         * @return paremt
         */
        public int getParent() {
            return this.parent;
        }
    
        public void setParent(int parent) {
            this.parent = parent;
        }
    
        /**
         * @hibernate.property
         * column="TITLE"
         * not-null="true"
         * @return title
         */
        public String getTitle() {
            return this.title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        /**
         * @hibernate.property
         * column="CONTENTS"
         * not-null="true"
         * @return contents
         */
        public String getContents() {
            return this.contents;
        }
    
        public void setContents(String contents) {
            this.contents = contents;
        }
    
        /**
         * @hibernate.property
         * column="REGIST_DATE"
         * not-null="true"
         * @return
         */
        public Date getRegistDate() {
            return this.registDate;
        }
    
        public void setRegistDate(Date registDate) {
            this.registDate = registDate;
        }
    
        /**
         * @hibernate.property
         * column="USER_ID"
         * not-null="true"
         * @return
         */
        public String getUserId() {
            return this.userId;
        }
    
        public void setUserId(String userId) {
            this.userId = userId;
        }
    
        /**
         * @hibernate.property
         * column="URL"
         * not-null="false"
         * @return
         */
        public String getUrl() {
            return this.url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        /**
         * @hibernate.property
         * column="E_MAIL"
         * not-null="true"
         * @return
         */
        public String getEmail() {
            return this.email;
        }
    
        public void setEmail(String EMail) {
            this.email = EMail;
        }
    
        public Integer getDeleteFlag() {
            return this.deleteFlag;
        }
    
        /**
         * @hibernate.property
         * column="DELETE_FLAG"
         * not-null="false"
         * @param deleteFlag
         */
        public void setDeleteFlag(Integer deleteFlag) {
            this.deleteFlag = deleteFlag;
        }
    
        public String toString() {
            return new ToStringBuilder(this).append("id", getId()).toString();
        }
    
        public boolean equals(Object other) {
            if (!(other instanceof BBS))
                return false;
            BBS castOther = (BBS) other;
            return new EqualsBuilder().append(this.getId(), castOther.getId())
                    .isEquals();
        }
    
        public int hashCode() {
            return new HashCodeBuilder().append(getId()).toHashCode();
        }
    
    }
    
                  
    
  • User.java
    package com.chikkun.jbbs.database;
    
    import java.io.Serializable;
    import org.apache.commons.lang.builder.EqualsBuilder;
    import org.apache.commons.lang.builder.HashCodeBuilder;
    import org.apache.commons.lang.builder.ToStringBuilder;
    
    /**
     * @author chikkun
     * @hibernate.class
     *   table="BBS"
     */
    public class User implements Serializable {
    
        /** identifier field */
        private Integer id;
    
        /** persistent field */
        private String username;
    
        /** persistent field */
        private String password;
    
        /** nullable persistent field */
        private String role;
    
        /** full constructor */
    public User(Integer id, String username, String password, String role) {
            this.id = id;
            this.username = username;
            this.password = password;
            this.role = role;
        }
    
        /** default constructor */
        public User() {
        }
    
        /** minimal constructor */
        public User(Integer id, String username, String password) {
            this.id = id;
            this.username = username;
            this.password = password;
        }
        /**
         *  @hibernate.id
         *  column="ID"
         *  unsaved-value="null"             
         *  generator-class="identity"
         * @hibernate.generator-param
         *    name="sequence"
         *    value="SEQ_USER"
         * 
         * @return Returns the id.
         */
        public Integer getId() {
            return this.id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        /**
         * @hibernate.property
         * column="USERNAME"
         * not-null="false"
         * @return
         */
        public String getUsername() {
            return this.username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        /**
         * @hibernate.property
         * column="PASSWORD"
         * not-null="false"
         * @return
         */
        public String getPassword() {
            return this.password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        /**
         * @hibernate.property
         * column="ROLE"
         * not-null="false"
         * @return
         */
        public String getRole() {
            return this.role;
        }
    
        public void setRole(String role) {
            this.role = role;
        }
    
        public String toString() {
            return new ToStringBuilder(this).append("id", getId()).toString();
        }
    
        public boolean equals(Object other) {
            if (!(other instanceof User))
                return false;
            User castOther = (User) other;
            return new EqualsBuilder().append(this.getId(), castOther.getId())
                    .isEquals();
        }
    
        public int hashCode() {
            return new HashCodeBuilder().append(getId()).toHashCode();
        }
    
    }
    
                  
    
  • build.xml---これはstrutsタスクのdependsをhibernateにして、そのhibernateを定義する。
      <!-- define targets. -->
      <target name="struts" depends="hibernate">
       ........   
      </target>
      ....
      <!--hbmファイルの作成-->
      <target name="hibernate">
        <taskdef name="xdoclet"
                 classname="xdoclet.modules.hibernate.HibernateDocletTask">
          <classpath refid="build.classpath" />
        </taskdef>
        <xdoclet destdir="${classes.dir}"
                 excludedtags="@version,@author,@param,@return"
                 force="false"
                 mergedir="WEB-INF/merge"
                 verbose="false"
                 encoding="${local.encoding}">
          <fileset dir="${src.dir}">
            <include name="**/*.java" />
          </fileset>
          <hibernate version="3.0" />
        </xdoclet>
      </target>
    
                  
    

これはmiddlegenの場合と反対方向なので、間違って、middlegenを実行すると、DTOが上書きされてしまいます。

strutsにおける値検証の概要

だいぶ寄り道をしてしまいましたが、strutsにおける、html上のフォームにおける値検証のポイントは以下です。

  • アクションフォームをActionFormクラスの代わりに、ValidatoFormクラスをextendsする。
  • struts-config.xmlのaction-mappingsの中に、例えば、

        <action
          path="/memberInfoEdit"
          type="com.chikkun.roomschedule.action.admin.MemberInfoEditAction"
          name="memberInfoForm"
          scope="request"
          input="/WEB-INF/template/admin/memberInfoConfirm.vm"
          roles="admin"
          unknown="false"
          validate="true"
        >
          <forward
            name="success"
            path="/WEB-INF/template/admin/memberInfoConfirm.vm"
            redirect="false"
          />
        </action>
    
                  
    

    のように、validateをtureにし、inputをしっかり書いておく(これは検証に引っかかった場合の遷移先になる---通常は入力する画面と同じになるけれど)。

  • pluginの設定をstruts-conf.xmlに書き込む(すでにmergeディレクトリーにある)。 ただし、あとでわかったことですが、strutsのバージョン1.3からvalidator-rules.xmlがjarファイル内に移動したので、merge/struts-plugins.xmlは以下のように修正する必要がある。
    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
        <set-property property="pathnames"
            value="/org/apache/struts/validator/validator-rules.xml,
                   /WEB-INF/validation.xml"/>
    </plug-in>
    
                  
    
  • validation.xmlの作成→ValidatorForm内にxdocletのタグという形で書き込んでいく。例えば、

        /**
         * @param contents 発言内容
         * @param string
         * @struts.validator type="required" msgkey="errors.required"
         * @struts.validator-args arg0resource="errors.comments"
         */
        public final void setContents(String contents) {
            this.contents = contents;
        }
    
                  
    

    のように、setterのJavadoc内に書きます。この場合は、必須で、エラーメッセージはpropertiesファイルのerrors.requiredに書いてあり、さらに、そのエラーメッセージには変数があるので(「errors.required={0}は必須です。」の{0}が変数)、arg0resourceという形で、やはりpropertiesの中に書き込んだerrors.commentsを指定してあります(「errors.comments=発言内容」という感じ)。

    そして、それをどんな検証があるかは、Struts Validatorを参考にし、さらに、xdocletのタグの書き方はこの辺を参考にすると良いでしょう。

  • 検証に引っかかったときのメッセージなどをpropertiesファイルに書き込みます(前にちょっと触れましたが)。実際にはmerge/struts-message-resources.xmlに

    <message-resources parameter="Resources.ApplicationResources"/>
    
                  
    

    と、書いてあるので、ファイルはWEB-INF/classes/Resources/ApplicationResources.propertiesというファイル名になる(具体的な内容等は後で)。また、日本語を使用する場合は、native2asciiでunicodeに変換する必要があります(これも後述)。

  • 当然、そのフォームを作成し、検証に引っかかった場合のエラーメッセージを表示する部分に、velocityのtoolboxで定義してある、$errorsを使う。

実際のクラスなど

これから作るのは次の3つ。

  • YAMLの設定ファイルから設定を読み取るためのクラス(Config.java)
  • 発言用のフォームをただ見せるためのAction(NewContributionShowFormAction.java)とActionForm(NewContributionActionForm.java)。
  • 発言用のフォームのテンプレート(contribution.vm)。
  • 検証等にひかかった時に表示するproperty(ApplicationResources.properties)。
  • 登録用のAction(NewContributionAction.java)とActionForm(NewContributionActionForm.java---これは表示用のと共用)

Config.java

以前も述べたように、日本語が通らないようなので、次のような方針で行く。

  • yamlファイルは「JBBS/conf/bbs.yml」におく。
  • これをnative2asciiで変換して、「WEB-INF/bbs.yml」に配置する。そのためにpom.xmlとbuild.xmlに次の部分を追加する。
    • pom.xml(後半のexecution)
          <plugins>
            <plugin>
              <artifactId>maven-antrun-plugin</artifactId>
              <dependencies>
                <dependency>
                  <groupId>sun.jdk</groupId>
                  <artifactId>tools</artifactId>
                  <version>1.5.0</version>
                  <scope>system</scope>
                  <systemPath>${env.JAVA_HOME}/lib/tools.jar</systemPath>
                </dependency>
                <dependency>
                  <groupId>ant</groupId>
                  <artifactId>ant-nodeps</artifactId>
                  <version>1.6.5</version>
                </dependency>
              </dependencies>
              <executions>
                <execution>
                  <!-- id タグは executions 内に複数の execution タグを含める場合に必要. -->
                  <id>web_xml</id>
                  <phase>compile</phase>
                  <configuration>
                    <tasks>
                      <!-- ここに Ant のタスクを記述する. -->
                      <ant antfile="build.xml" target="web" inheritRefs="true" />
                    </tasks>
                  </configuration>
                  <goals>
                    <goal>run</goal>
                  </goals>
                </execution>
                <execution>
                  <!-- id タグは executions 内に複数の execution タグを含める場合に必要. -->
                  <id>native2ascii</id>
                  <phase>process-resources</phase>
                  <configuration>
                    <tasks>
                      <!-- ここに Ant のタスクを記述する.
                           ここでは別ファイルとして用意した build.xml の native2ascii を実行する. -->
      <ant antfile="build.xml" target="native2ascii" inheritRefs="true" />
                    </tasks>
                  </configuration>
                  <goals>
                    <goal>run</goal>
                  </goals>
                </execution>
              </executions>
            </plugin>
          </plugins>
      
                          
      
    • build.xml(properties用の部分もある!)
        <target name="native2ascii">
          <mkdir dir="${classes.dir}" />
          <delete dir="${classes.dir}" includes="**/*.properties" />
          <native2ascii src="${resources.dir}" includes="**/*.properties"
                        dest="${classes.dir}" encoding="${local.encoding}" />
          <native2ascii src="${conf.dir}" includes="**/*.yml"
                        dest="${target.web_inf}" encoding="${local.encoding}" />
        </target>
      
                          
      
    • native2asciiでunicodeなっているものを元に戻すのは、Config.javaの中で行う。デフォルトもこの中で、設定する。それd実際のクラスは
      /**
       * 
       */
      package com.chikkun.jbbs;
      
      import java.io.FileNotFoundException;
      import java.io.FileReader;
      import java.util.Map;
      
      import org.jvyaml.YAML;
      
      /**
       * @author chikkun
       * 
       */
      public class Config {
      
          private Map conf;
          private Map bbs;
          private Map cont;
          private Map template;
      
          private String charset;
          private String BBSTitle;
          private String stylesheet;
          private boolean deleteRadioFlag;
          private String deleteOnLabel;
          private String deleteOffLabel;
          private String randomNum;
          private String contNameLabel;
          private String contEmailLabel;
          private String contTitleLabel;
          private String contContLabel;
          /**
           * 
           */
          public Config(String y) {
              try {
                  conf = (Map) YAML.load(new FileReader(y));
                  bbs = (Map) conf.get("bbs");
                  cont = (Map) conf.get("cont");
                  template = (Map) conf.get("template");
              } catch (FileNotFoundException e) {
                  // TODO 自動生成された catch ブロック
                  e.printStackTrace();
              }
          }
      
          /**
           * @return bBSTitle
           */
          public final String getBBSTitle() {
              if (null == BBSTitle) {
      
                  String tmp = (String) bbs.get("title");
                  if (tmp == null) {
                      tmp = "みんなの会議室";
                  }
                  this.setBBSTitle(this.loadConvert(tmp));
              }
              return BBSTitle;
          }
          /**
           * @return charset
           */
          public final String getCharset() {
              if (null == charset) {
                  String tmp = (String) bbs.get("charset");
                  if (tmp == null) {
                      tmp = "Windows-31J";
                  }
                  this.setCharset(tmp);
              }
              return charset;
          }
          /**
           * @return deleteOffLabel
           */
          public final String getDeleteOffLabel() {
              if (null == deleteOffLabel) {
                  String tmp = (String) cont.get("delete_off_label");
                  if (tmp == null) {
                      tmp = "表示する";
                  }
                  this.setDeleteOffLabel(this.loadConvert(tmp));
              }
              return deleteOffLabel;
          }
          /**
           * @return deleteOnLabel
           */
          public final String getDeleteOnLabel() {
              if (null == deleteOnLabel) {
                  String tmp = (String) cont.get("delete_on_label");
                  if (tmp == null) {
                      tmp = "表示しない";
                  }
                  this.setDeleteOnLabel(this.loadConvert(tmp));
              }
              return deleteOnLabel;
          }
          /**
           * @return deleteRadioFlag
           */
          public final boolean getDeleteRadioFlag() {
              String tmp = (String) cont.get("delete_radio_flag");
              // 結局これで何も書かれていないと、trueになってしまっている!
              if (null == tmp || tmp.equals("1")) {
                  deleteRadioFlag = true;
              } else {
                  deleteRadioFlag = false;
              }
              return deleteRadioFlag;
          }
          /**
           * @return randomNum
           */
          public final String getRandomNum() {
              if (null == randomNum) {
                  String tmp = (String) bbs.get("random_num");
                  if (tmp == null) {
                      tmp = "2";
                  }
      
                  this.setRandomNum(tmp);
              }
              return randomNum;
          }
          /**
           * @return stylesheet
           */
          public final String getStylesheet() {
              if (null == stylesheet) {
                  String tmp = (String) template.get("t_stylesheet");
                  if (tmp == null) {
                      tmp = "default.css";
                  }
                  this.setStylesheet(tmp);
              };
              return stylesheet;
          }
      
          /**
           * @return contEmailLabel
           */
          public final String getContEmailLabel() {
              if (null == contEmailLabel) {
                  String tmp = (String) cont.get("email");
                  if (tmp == null) {
                      tmp = "メール";
                  }
                  this.setContEmailLabel(this.loadConvert(tmp));
              };
      
              return contEmailLabel;
          }
      
          /**
           * @return contNameLabel
           */
          public final String getContNameLabel() {
              if (null == contNameLabel) {
                  String tmp = (String) cont.get("name");
                  if (tmp == null) {
                      tmp = "氏名";
                  }
                  this.setContNameLabel(this.loadConvert(tmp));
              };
      
              return contNameLabel;
          }
      
          /**
           * @return contTitleLabel
           */
          public final String getContTitleLabel() {
              if (null == contTitleLabel) {
                  String tmp = (String) cont.get("title");
                  if (tmp == null) {
                      tmp = "題名";
                  }
                  this.setContTitleLabel(this.loadConvert(tmp));
              };
              return contTitleLabel;
          }
      
          /**
           * @return contContLabel
           */
          public final String getContContLabel() {
              if (null == contContLabel) {
                  String tmp = (String) cont.get("cont");
                  if (tmp == null) {
                      tmp = "内容";
                  }
                  this.setContContLabel(this.loadConvert(tmp));
              };
              return contContLabel;
          }
      
          // ///////////setter
          /**
           * @param title
           *            設定する bBSTitle
           */
          public final void setBBSTitle(String title) {
              BBSTitle = title;
          }
          /**
           * @param charset
           *            設定する charset
           */
          public final void setCharset(String charset) {
              this.charset = charset;
          }
          /**
           * @param deleteOffLabel
           *            設定する deleteOffLabel
           */
          public final void setDeleteOffLabel(String deleteOffLabel) {
              this.deleteOffLabel = deleteOffLabel;
          }
          /**
           * @param deleteOnLabel
           *            設定する deleteOnLabel
           */
          public final void setDeleteOnLabel(String deleteOnLabel) {
              this.deleteOnLabel = deleteOnLabel;
          }
          /**
           * @param deleteRadioFlag
           *            設定する deleteRadioFlag
           */
          public final void setDeleteRadioFlag(boolean deleteRadioFlag) {
              this.deleteRadioFlag = deleteRadioFlag;
          }
          /**
           * @param randomNum
           *            設定する randomNum
           */
          public final void setRandomNum(String randomNum) {
              this.randomNum = randomNum;
          }
          /**
           * @param stylesheet
           *            設定する stylesheet
           */
          public final void setStylesheet(String stylesheet) {
              this.stylesheet = stylesheet;
          }
      
          /**
           * @param contContLabel
           *            設定する contConLabel
           */
          public final void setContContLabel(String _contContLabel) {
              this.contContLabel = _contContLabel;
          }
      
          // //////////トップのセクション
          /**
           * @return bbs
           */
          public final Map getBbs() {
              return bbs;
          }
          /**
           * @return conf
           */
          public final Map getConf() {
              return conf;
          }
          /**
           * @return cont
           */
          public final Map getCont() {
              return cont;
          }
          /**
           * @return template
           */
          public final Map getTemplate() {
              return template;
          }
          /**
           * @param bbs
           *            設定する bbs
           */
          public final void setBbs(Map bbs) {
              this.bbs = bbs;
          }
          /**
           * @param conf
           *            設定する conf
           */
          public final void setConf(Map conf) {
              this.conf = conf;
          }
          /**
           * @param cont
           *            設定する cont
           */
          public final void setCont(Map cont) {
              this.cont = cont;
          }
          /**
           * @param template
           *            設定する template
           */
          public final void setTemplate(Map template) {
              this.template = template;
          }
      
          /**
           * @param contEmailLabel
           *            設定する contEmailLabel
           */
          public final void setContEmailLabel(String contEmailLabel) {
              this.contEmailLabel = contEmailLabel;
          }
      
          /**
           * @param contNameLabel
           *            設定する contNameLabel
           */
          public final void setContNameLabel(String contNameLabel) {
              this.contNameLabel = contNameLabel;
          }
      
          /**
           * @param contTitleLabel
           *            設定する contTitleLabel
           */
          public final void setContTitleLabel(String contTitleLabel) {
              this.contTitleLabel = contTitleLabel;
          }
      
          private String loadConvert(String theString) {
              char aChar;
              int len = theString.length();
              StringBuffer outBuffer = new StringBuffer(len);
      
              for (int x = 0; x < len;) {
                  aChar = theString.charAt(x++);
                  if (aChar == '\\') {
                      aChar = theString.charAt(x++);
                      if (aChar == 'u') {
                          // Read the xxxx
                          int value = 0;
                          for (int i = 0; i < 4; i++) {
                              aChar = theString.charAt(x++);
                              switch (aChar) {
                                  case '0' :
                                  case '1' :
                                  case '2' :
                                  case '3' :
                                  case '4' :
                                  case '5' :
                                  case '6' :
                                  case '7' :
                                  case '8' :
                                  case '9' :
                                      value = (value << 4) + aChar - '0';
                                      break;
                                  case 'a' :
                                  case 'b' :
                                  case 'c' :
                                  case 'd' :
                                  case 'e' :
                                  case 'f' :
                                      value = (value << 4) + 10 + aChar - 'a';
                                      break;
                                  case 'A' :
                                  case 'B' :
                                  case 'C' :
                                  case 'D' :
                                  case 'E' :
                                  case 'F' :
                                      value = (value << 4) + 10 + aChar - 'A';
                                      break;
                                  default :
                                      throw new IllegalArgumentException(
                                              "Malformed \\uxxxx encoding.");
                              }
                          }
                          outBuffer.append((char) value);
                      } else {
                          if (aChar == 't')
                              aChar = '\t';
                          else if (aChar == 'r')
                              aChar = '\r';
                          else if (aChar == 'n')
                              aChar = '\n';
                          else if (aChar == 'f')
                              aChar = '\f';
                          outBuffer.append(aChar);
                      }
                  } else
                      outBuffer.append(aChar);
              }
              return outBuffer.toString();
          }
      
      }
      
                          
      

表示用のアクションとアクションフォーム

アクションはほとんど何もないクラスになりますが、アクションフォームは登録の時使うのと共有するために、ValidatorFormをextendsして作成します。ただし、validateをfalseにしておきます(そうしないとエラーメッセージが出てしまいます)。さらに、ちょっと先回りですが登録の際、validateにひっかかるとActionを実行されずに、inputで指定したテンプレートにforwardさせられるので、YAMLから読み込んだラベルなどをformに設定するのをActionで行うと(普通はそうなんですが)ラベルなどがNULLになってしまいます(つまりテンプレート内のラベルが何も出ていないようなことが起こる)。なのdValidatorFormのvalidateメソッド(実際はvalidateはpluginのvalidatorで行っているので、なにもvalidateしていませんが)で値を設定しています。

  • NewContributionShowFormAction(フォームを最初に表示)
    package com.chikkun.jbbs;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.struts.action.Action;
    import org.apache.struts.action.ActionForm;
    import org.apache.struts.action.ActionForward;
    import org.apache.struts.action.ActionMapping;
    
    /**
     * @struts.action name="newContributionForm"  path="/showContributionForm"
     *                                       validate="false" scope="request"
    * @struts.action-forward name="success" \
        path="/WEB-INF/template/contribution.vm"
     * 
     * @author chikkun
     */
    public class NewContributionShowFormAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm actionForm,
                HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
    
    NewContributionActionForm form = (NewContributionActionForm) actionForm;
    String y = \
        getServlet().getServletContext().getRealPath("/WEB-INF/bbs.yml");
            Config config = new Config(y);
    
            
            form.setBBSTitle(config.getBBSTitle());
            
            form.setCharset(config.getCharset());
            //form.setDeleteFlag();
            form.setDeleteOffLabel(config.getDeleteOffLabel());
            form.setDeleteOnLabel(config.getDeleteOnLabel());
            form.setContEmailLabel(config.getContEmailLabel());
            form.setContNameLabel(config.getContNameLabel());
            form.setContTitleLabel(config.getContTitleLabel());
            form.setContContLabel(config.getContContLabel());
            
            return (mapping.findForward("success"));
        }
    }
    
                    
    
  • /**
     * 
     */
    package com.chikkun.jbbs;
    
    import java.util.Date;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.struts.action.ActionErrors;
    import org.apache.struts.action.ActionMapping;
    import org.apache.struts.validator.ValidatorForm;
    
    /**
     * @struts.form name="newContributionForm"
     * @author chikkun
     *
     */
    public class NewContributionActionForm extends ValidatorForm {
    
        private String charset;
    
        private String BBSTitle;
    
        private String stylesheet;
    
        private int parent;
    
        private int contId;
    
        private boolean edit = false;
    
        /**
         * 編集の場合の発言番号
         */
        private int key;
    
        private Date registDate;
    
        /**
         * 削除フラグを見せるかどうか?
         */
        private boolean deleteCheckOn = false;
    
        private String contNameLabel;
    
        private String origUserId;
    
        private String contEmailLabel;
    
        private String email;
    
        private String contTitleLabel;
    
        private String origTitle;
    
        private String title;
    
        private String contContLabel;
    
        private String origContents;
    
        private String contents;
    
        private String deleteFlag;
    
        private String deleteOnLabel;
    
        private String deleteOffLabel;
    
        private String random;
    
        private String userId;
    
    public ActionErrors validate(ActionMapping mapping, HttpServletRequest req) \
        {
            ActionErrors errors = super.validate(mapping, req);
    
            String y = getServlet().getServletContext().getRealPath(
                    "/WEB-INF/bbs.yml");
            Config config = new Config(y);
    
            this.setBBSTitle(config.getBBSTitle());
    
            this.setCharset(config.getCharset());
            //form.setDeleteFlag();
            this.setDeleteOffLabel(config.getDeleteOffLabel());
            this.setDeleteOnLabel(config.getDeleteOnLabel());
            this.setContEmailLabel(config.getContEmailLabel());
            this.setContNameLabel(config.getContNameLabel());
            this.setContTitleLabel(config.getContTitleLabel());
            this.setContContLabel(config.getContContLabel());
    
            return errors;
        }
        /**
         * @return name
         */
        public final String getUserId() {
            return userId;
        }
    
        public final String getBBSTitle() {
            return BBSTitle;
        }
    
        public final String getCharset() {
            return charset;
        }
    
        public final String getContContLabel() {
            return contContLabel;
        }
    
        public final String getContEmailLabel() {
            return contEmailLabel;
        }
    
        public final String getContents() {
            return contents;
        }
    
        public final int getContId() {
            return contId;
        }
    
        public final String getContNameLabel() {
            return contNameLabel;
        }
    
        public final String getContTitleLabel() {
            return contTitleLabel;
        }
    
        public final Date getRegistDate() {
            return registDate;
        }
    
        public final boolean isDeleteCheckOn() {
            return deleteCheckOn;
        }
    
        public final String getDeleteFlag() {
            return deleteFlag;
        }
    
        public final String getDeleteOffLabel() {
            return deleteOffLabel;
        }
    
        public final String getDeleteOnLabel() {
            return deleteOnLabel;
        }
    
        public final boolean isEdit() {
            return edit;
        }
    
        public final String getEmail() {
            return email;
        }
    
        public final int getKey() {
            return key;
        }
    
        public final String getOrigContents() {
            return origContents;
        }
    
        public final String getOrigTitle() {
            return origTitle;
        }
    
        public final String getOrigUserId() {
            return origUserId;
        }
    
        public final int getParent() {
            return parent;
        }
    
        public final String getRandom() {
            return random;
        }
    
        public final String getStylesheet() {
            return stylesheet;
        }
    
        public final String getTitle() {
            return title;
        }
    
        ////////////setter
    
        /**
         * @param name 設定する userId
         * @struts.validator type="required" msgkey="errors.required"
         * @struts.validator-args arg0resource="errors.name"
         * @struts.validator type="maxlength" msgkey="errors.maxlength"
    * @struts.validator-args arg0resource="errors.comments" \
        arg1value="${var:maxlength}"
         * @struts.validator-var name="maxlength" value="20"
         * @struts.validator type="mask" msgkey="errors.hankaku"
         * @struts.validator-var name="mask" value="^[-a-zA-Z_0-9()]+$"
         * @struts.validator-args arg0resource="errors.name"
         */
        public final void setUserId(String name) {
            this.userId = name;
        }
    
        public final void setBBSTitle(String title) {
            BBSTitle = title;
        }
    
        public final void setCharset(String charset) {
            this.charset = charset;
        }
    
        public final void setContContLabel(String contContLabel) {
            this.contContLabel = contContLabel;
        }
    
        public final void setContEmailLabel(String contEmailLabel) {
            this.contEmailLabel = contEmailLabel;
        }
    
        /**
         * @param contents 発言内容
         * @param string
         * @struts.validator type="required" msgkey="errors.required"
         * @struts.validator-args arg0resource="errors.comments"
         * @struts.validator type="maxlength" msgkey="errors.maxlength"
    * @struts.validator-args arg0resource="errors.comments" \
        arg1value="${var:maxlength}"
         * @struts.validator-var name="maxlength" value="2000"
         */
        public final void setContents(String contents) {
            this.contents = contents;
        }
    
        public final void setContId(int contId) {
            this.contId = contId;
        }
    
        public final void setContNameLabel(String contNameLabel) {
            this.contNameLabel = contNameLabel;
        }
    
        public final void setContTitleLabel(String contTitleLabel) {
            this.contTitleLabel = contTitleLabel;
        }
    
        public final void setRegistDate(Date date) {
            this.registDate = date;
        }
    
        public final void setDeleteCheckOn(boolean deleteCheckOn) {
            this.deleteCheckOn = deleteCheckOn;
        }
    
        public final void setDeleteFlag(String deleteFlag) {
            this.deleteFlag = deleteFlag;
        }
    
        public final void setDeleteOffLabel(String deleteOffLabel) {
            this.deleteOffLabel = deleteOffLabel;
        }
    
        public final void setDeleteOnLabel(String deleteOnLabel) {
            this.deleteOnLabel = deleteOnLabel;
        }
    
        public final void setEdit(boolean edit) {
            this.edit = edit;
        }
    
        /**
         * @param mail
         * @struts.validator type="email" msgkey="errors.email"
         * @struts.validator-args arg0resource="errors.mail"
         */
        public final void setEmail(String mail) {
            email = mail;
        }
    
        public final void setKey(int key) {
            this.key = key;
        }
    
        public final void setOrigContents(String origContents) {
            this.origContents = origContents;
        }
    
        public final void setOrigTitle(String origTitle) {
            this.origTitle = origTitle;
        }
    
        public final void setOrigUserId(String origUserId) {
            this.origUserId = origUserId;
        }
    
        public final void setParent(int parent) {
            this.parent = parent;
        }
    
        public final void setRandom(String random) {
            this.random = random;
        }
    
        public final void setStylesheet(String stylesheet) {
            this.stylesheet = stylesheet;
        }
        /**
         * @param title 題名
         * @param string
         * @struts.validator type="required" msgkey="errors.required"
         * @struts.validator-args arg0resource="errors.title"
         * @struts.validator type="maxlength" msgkey="errors.maxlength"
    * @struts.validator-args arg0resource="errors.title" \
        arg1value="${var:maxlength}"
         * @struts.validator-var name="maxlength" value="30"
         */
        public final void setTitle(String title) {
            this.title = title;
        }
    }
    
                    
    

テンプレート(contribution.vm)

Velocityのテンプレートを作成。Perlの場合のテンプレートの変数名がPerl式のアンスコが多いのと、どうも変な変数名もあったので適当に変数名は変えていあり、あまりにもタブと空白が混ざっていたり、インデントが変だったのでその辺も修正しました(一部まだ戻るボタンなどを作成していないので、未完成な部分もあります)。

sa<?xml version="1.0" encoding="Shift_Jis"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
          "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html lang="ja">
  <head>
    <title>$form.bean.getBBSTitle()</title>
<meta content="text/html;charset=$form.bean.getCharset()" \
    http-equiv="Content-Type" />
<link href="$form.bean.getStylesheet()" rel="stylesheet" type="text/css" />
  </head>
<body lang="ja" bgcolor="#EAEAEA" topmargin="0" leftmargin="0" \
    marginheight="0" marginwidth="0">
<table width="780" height="100%" border="0" cellpadding="0" cellspacing="0" \
    bgcolor="#FFFFFF">
      <tr>
        <td  valign="top" width="760">
          <p align="center">
            <img src="../img/title.jpg"/>
          </p>
          <center>
            <br/>
<form action="$link.setAction('/newContribution')" \
    charset="$form.bean.getCharset()" class="fb_form" id="cont" lang="ja" \
    method="post" name="cont">
              <div class="fb_state" id="cont_state">
              </div>
<input id="_submitted_cont" name="_submitted_cont" type="hidden" value="1" \
    />
<input class="fb_hidden" id="parent" name="parent" type="hidden" \
    value="$!form.bean.getParent()" />
<input class="fb_hidden" id="kind_of_work" name="kind_of_work" \
    type="hidden" value="save" />
<input class="fb_hidden" id="cont_id" name="cont_id" type="hidden" \
    value="$!form.bean.getContId" />
              #if($form.bean.getEdit())
<input class="fb_hidden" id="key" name="key" type="hidden" \
    value="$!form.bean.getKey()" />
<input class="fb_hidden" id="date" name="registDate" type="hidden" \
    value="$!form.bean.getRegistDate()" />
<input class="fb_hidden" id="edit" name="edit" type="hidden" value="1" />
              #end
<input class="fb_hidden" id="delete_check_on" name="deleteCheckOn" \
    type="hidden" value="$!form.bean.getDeleteCheckOn()"/>
<table width="600" cellpadding="5" cellspacing="1" bgcolor="#BADB73" \
    class="fb" id="cont_body">
                <tr>
                  <td  colspan="2" nowrap align="center">
                    <font color="#FFFFFF">
                      <b>$form.bean.getBBSTitle()</b>
                    </font>
                  </td>
                </tr>
                <tr id="cont_cont_name_row">
<td nowrap width="30%" align="center" bgcolor="#E4F3C7" class="fb_label" \
    id="cont_cont_name_label">
                    $form.bean.getContNameLabel()
                  </td>
<td width="70%" align="left" bgcolor="#FFFFFF" class="fb_field" \
    id="cont_cont_name_field">
<input class="fb_input" size="30" id="cont_name" name="userId" type="text" \
    value="$!form.bean.getUserId()" />
                    <span class="fb_comment">
                      $!errors.getMsgs('userId')
                    </span>
                  </td>
                </tr>
                <tr id="cont_cont_email_row">
<td nowrap width="30%" align="center" bgcolor="#E4F3C7" class="fb_label" \
    id="cont_cont_email_label">
                    $form.bean.getContEmailLabel()
                  </td>
<td width="70%" align="left" bgcolor="#FFFFFF" class="fb_field" \
    id="cont_cont_email_field">
<input class="fb_input" size="30" id="cont_email" name="email" type="text" \
    value="$!form.bean.getEmail()" />
                    <span class="fb_comment">
                      $!errors.getMsgs('email')
                    </span>
                  </td>
                </tr>
                <tr id="cont_cont_title_row">
<td nowrap width="30%" align="center" bgcolor="#E4F3C7" class="fb_label" \
    id="cont_cont_title_label">
                    $form.bean.getContTitleLabel()
                  </td>
<td width="70%" align="left" bgcolor="#FFFFFF" class="fb_field" \
    id="cont_cont_title_field">
<input class="fb_input" size="30" id="cont_title" name="title" type="text" \
    value="$!form.bean.getTitle()"/>
                    <span class="fb_comment">
                      $!errors.getMsgs('title')
                    </span>
                  </td>
                </tr>
                <tr id="cont_cont_cont_row">
<td nowrap width="30%" align="center" bgcolor="#E4F3C7" class="fb_label" \
    id="cont_cont_cont_label">
                    $form.bean.getContContLabel()
                  </td>
<td width="70%" align="left" bgcolor="#FFFFFF" class="fb_field" \
    id="cont_cont_cont_field">
<textarea class="fb_textarea" cols="80" id="cont_cont" name="contents" \
    rows="18">
                      $!form.bean.getContents
                    </textarea>
                    <span class="fb_comment">
                      $errors.getMsgs('contents')
                    </span>
                  </td>
                </tr>
                #if($form.bean.getEdit())
                  #if($form.bean.getDeleteRadioFlag())
                <tr id="cont_cont_delete_row">
<td nowrap width="30%" align="center" bgcolor="#E4F3C7" class="fb_label" \
    id="cont_cont_delete_label">
                    表示
                  </td>
<td width="70%" align="left" bgcolor="#FFFFFF" class="fb_field" \
    id="cont_cont_delete_field">
                    #if($form.bean.getDeleteFlag())
<INPUT type="radio" name="radio_delete" value="1" checked/>
                    $form.bean.getDeleteOnLabel()
                    <INPUT type="radio" name="radio_delete" value="0"/>
                    $form.bean.getDeleteOffLabel()
                    #else
                    <INPUT type="radio" name="radio_delete" value="1"/>
                    $form.bean.getDeleteOnLabel()
<INPUT type="radio" name="radio_delete" value="0" checked/>
                    $form.bean.getDeleteOffLabel()
                    #end
                  </td>
                </tr>
                  #end
                #end
              </table>
              <br/>
              #if($form.bean.getRandom())
              <img src='testImage.cgi?task=$form.bean.getRandom()'/>
              <br/>
              <font color="blue">画像中の数値を入力して下さい</font>
              <br/>
<input type="hidden" name="random_num" value="$form.bean.getRandom()"/>
              <input type="text" name="duplicate_random"/>
              <br/>
              <span class="fb_comment">
                $errors.getMsgs('random')
              </span>
              <br/>
              #end
              #if($form.bean.getEdit())
<input src="../img/button_hensyu.gif" class="fb_button" id="cont_submit" \
    name="_submit" type="image" value="書込む" />
              #else
<input src="../img/button_run.gif" class="fb_button" id="cont_submit" \
    name="_submit" type="image" value="書込む" />
              #end
            </form>
            <form action="[% main_cgi %]" method="post">
              <input name="return" type="submit" value="戻る" />
              <input name="kind_of_work" type="hidden" value="list" />
<input name="deleteCheckOn" type="hidden" \
    value="$!form.bean.getDeleteCheckOn()" />
            </form>
          </center>
        </td>
        <td width="20" background="../img/shadow.gif">
          <img src="../img/spacer.gif"/>
        </td>
      </tr>
    </table>
  </body>
</html>

            

JBBS/src/main/resources/Resources/ApplicationResources.properties

エラーメッセージ等を以下のように書き込む。実際にはこれをnative2asciiにて変換する(build.xmすでに述べた)。

system.charset=Windows-31J
errors.required={0}は必須です。
errors.email={0}の形式がおかしいようです。
errors.hankaku={0}は半角英数にしてください。
errors.name=氏名
errors.mail=メールアドレス
errors.title=発言のタイトル
errors.comments=発言内容
errors.maxlength={0}は{1}文字以下で入力してください。
errors.header=<font color="red">
errors.footer=</font>

NewContributionAction.javaとNewContributionActionForm.java

いよいよ登録用のアクションです。アクションフォームは、前に出てきたものと同じです。hibernateを使って登録します。そうそう、成功した場合の臨時のtmp.vmも用意します。アクションの中で、HTMLのフォームの変数とアクションフォームの変数と、さらにDTOの変数が同じだと、BeanUtilsというApache Commonsにあるライブラリーを使うと、beanからbeanへ値をコピーするのが楽です。なので、これをpom.xmlに登録する必要があります。

  • pom.xml
        <dependency>
          <groupId>velocity-tools</groupId>
          <artifactId>velocity-tools-view</artifactId>
          <version>1.3</version>
        </dependency>
    
    		
    
  • NewContributionAction
    package com.chikkun.jbbs;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.util.Date;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.beanutils.BeanUtils;
    import org.apache.struts.action.Action;
    import org.apache.struts.action.ActionForm;
    import org.apache.struts.action.ActionForward;
    import org.apache.struts.action.ActionMapping;
    
    import com.chikkun.jbbs.database.BBS;
    import com.chikkun.jbbs.database.BBSDAO;
    
    /**
     * @struts.action name="newContributionForm" path="/newContribution"
    * scope="request" validate="true" input="/WEB-INF/template/contribution.vm"
     * @struts.action-forward name="success" path="/WEB-INF/template/tmp.vm"
     * 
     * @author chikkun
     */
    public class NewContributionAction extends Action {
    
        /**
         * Performs the simple action
         * 
         * @param mapping
    * the action mappings where you find the return value of the
         *            forward
         * @param actionForm
    * the action form used, in this example it will be an instance
         *            of the DynaActionForm class
         * @param request
         *            the HTTP servlet request
         * @param response
         *            the HTTP servlet response
         */
    public ActionForward execute(ActionMapping mapping, ActionForm actionForm,
                HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
    NewContributionActionForm form = (NewContributionActionForm) actionForm;
            
            BBS bbs = new BBS();
            try {
                BeanUtils.copyProperties(bbs, form);
            } catch (IllegalAccessException e) {
                // TODO 自動生成された catch ブロック
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO 自動生成された catch ブロック
                e.printStackTrace();
            }
            bbs.setParent(0);
            bbs.setRegistDate(new Date());
            bbs.setDeleteFlag(new Integer(0));
            BBSDAO dao = new BBSDAO();
            dao.save(bbs);
            return (mapping.findForward("success"));
        }
    }
    
    		
    
  • tmp.vm
    <html>
    <body>
    <h2>Success</h2>
    
    Registed Successfully!!!!!!!!!!!
    
    </body>
    </html>
    
    		
    

さあ、http://localhost:8080/JBBS/action/showContributionFormにアクセスできて、うまく登録できればOK。これでうまくいくかなあ〜。念のため、ソース等をここからダウンロードできます。

SpringでHibernate、およびstruts

まずはHibernateから

SpringにHibernateのsessionを管理させると「遅延ローディング---lazy」というメリットを享受できます。これは簡単に言うと、親子関係のあるテーブルの親を検索した場合、その子供を参照しに行かない限り、テーブルから実際のデータを取りに行かず、その分メモリーやスピードの面などで大きなメリットを教授できます。

これを実現するには「Open Session in View」という方法を使います。しかし心配無用。Springには

	    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter  
	    

が用意されています。これを使いましょう。Xdocletのmergeディレクトリーにfilters.xmlというファイルを作成し、中に

<filter>
    <filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>

	    

次に、spring用のapplicationContext.xml内に

<?xml version="1.0" encoding="Windows-31J"?>

<!DOCTYPE beans PUBLIC
    "-//SPRING//DTD BEAN//EN"
    "spring-beans.dtd">
<beans default-autowire="no" default-lazy-init="false" \
    default-dependency-check="none">
    <!-- データソース -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" \
    destroy-method="close">
        <property name="driverClassName">
            <value>org.postgresql.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:postgresql://localhost:5432/DUM_BBS</value>
        </property>
        <property name="username">
            <value>chikkun</value>
        </property>
        <property name="password">
            <value>kazukun</value>
        </property>
    </bean>
    <!-- セッションファクトリ  -->
<bean id="sessionFactory" \
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation">
            <value>WEB-INF/classes/hibernate.cfg.xml</value>
        </property>
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
    </bean>
</beans>

	    

と、書き込みます。以前のBBSDAOは

package com.chikkun.jbbs.database;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Expression;

/**
 * @author chikkun
 */
public class BBSDAO extends BaseDAO {

    public void delete(BBS bb_) {
        Session session = null;
        Transaction transaction = null;
        try {
            session = getSession();
            transaction = session.beginTransaction();
            session.delete(bb_);
            transaction.commit();
        } catch (HibernateException ex) {
            //logger.info("データベースエラー:" + ex.getMessage());
            if (transaction != null) {
                try {
                    transaction.rollback();
                } catch (HibernateException ignore) {
                }
            }
            throw new RuntimeException(ex.getMessage());
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (HibernateException ignore) {
                }
            }
        }
    }

    public void save(BBS bb_) {
        Session session = null;
        Transaction transaction = null;
        try {
            session = getSession();
            transaction = session.beginTransaction();

            session.save(bb_);
            transaction.commit();
        } catch (HibernateException ex) {
            //logger.info("データベースエラー:" + ex.getMessage());
            if (transaction != null) {
                try {
                    transaction.rollback();
                } catch (HibernateException ignore) {
                }
            }
            throw new RuntimeException(ex.getMessage());
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (HibernateException ignore) {
                }
            }
        }
    }

    public void saveOrUpdate(BBS bb_) {
        Session session = null;
        Transaction transaction = null;
        try {
            session = getSession();
            transaction = session.beginTransaction();

            session.saveOrUpdate(bb_);
            transaction.commit();
        } catch (HibernateException ex) {
            //logger.info("データベースエラー:" + ex.getMessage());
            if (transaction != null) {
                try {
                    transaction.rollback();
                } catch (HibernateException ignore) {
                }
            }
            throw new RuntimeException(ex.getMessage());
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (HibernateException ignore) {
                }
            }
        }
    }

    public void update(BBS bb_) {
        Session session = null;
        Transaction transaction = null;
        try {
            session = getSession();
            transaction = session.beginTransaction();
            session.update(bb_);
            transaction.commit();
        } catch (HibernateException ex) {
            //logger.info("データベースエラー:" + ex.getMessage());
            if (transaction != null) {
                try {
                    transaction.rollback();
                } catch (HibernateException ignore) {
                }
            }
            throw new RuntimeException(ex.getMessage());
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (HibernateException ignore) {
                }
            }
        }
    }


    public List findById(Integer id) {
        List result = null;
        Session session = null;
        try {
            session = getSession();
            Criteria criteria = session.createCriteria(BBS.class);
            if (id != null) {
                criteria.add(Expression.eq("id", id));
            }
            result = criteria.list();
        } catch (HibernateException ex) {
            //logger.info("データベースエラー:" + ex.getMessage());
            throw new RuntimeException(ex.getMessage());
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (HibernateException ignore) {
                }
            }
        }
        return result;
    }

    public List findAll() {
        List result;
        Session session = null;
        try {
            session = getSession();
            Criteria criteria = session.createCriteria(BBS.class);

            result = criteria.list();
        } catch (HibernateException ex) {
            //logger.info("データベースエラー:" + ex.getMessage());
            throw new RuntimeException(ex.getMessage());
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (HibernateException ignore) {
                }
            }
        }
        return result;
    }
}

	    

とtransactionやらtry and catch .. finallyなどを書く必要があったわけですが、springに大半を任せることにより、次のようになります。

package com.chikkun.jbbs.database;

import java.util.List;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
 * @spring.bean name="bdao"
 * @spring.property name="sessionFactory" ref="sessionFactory"
 * @author chikkun
 */
public class BBSDAO extends HibernateDaoSupport implements IBBSDAO {

    public void delete(BBS bb_) {
        getHibernateTemplate().delete(bb_);
    }

    public void save(BBS bb_) {
        getHibernateTemplate().save(bb_);    }

    public void saveOrUpdate(BBS bb_) {
        getHibernateTemplate().saveOrUpdate(bb_);    }

    public void update(BBS bb_) {
        getHibernateTemplate().update(bb_);
    }


    public List findById(Integer id) {
        List result = null;
        result = getHibernateTemplate().find(
            "from bbs as bbs where bbs.id = ?", id);
        return result;
    }

    public List findAll() {
        return getHibernateTemplate().loadAll(BBS.class);
    }
}

	    

となりスッキリします。ただ、Springのbeanの定義が必要なので、

 * @spring.bean name="bdao"
 * @spring.property name="sessionFactory" ref="sessionFactory"

	    

というxdocletのタグが必要になり、これで

  <bean
      name="bdao"
      class="com.chikkun.jbbs.database.BBSDAO"
  >

    <property name="sessionFactory">
      <ref bean="sessionFactory"/>
    </property>
  </bean>

	    

が、xmlに書き出されます。そうそう、ただしこのためにはbuild.xml内に新たな定義が必要です。

  <target name="spring" description="make webcms-servlet.xml for spring">
    <delete failonerror="false">
      <fileset dir="${target.web_inf}" includes="webcms-servlet.xml"/>
    </delete>
    <delete failonerror="false">
      <fileset dir="${src.web_inf}" includes="bbs-servlet.xml"/>
    </delete>
<taskdef name="springdoclet" \
    classname="xdoclet.modules.spring.SpringDocletTask"
      classpathref="build.classpath"/>
    <springdoclet destdir="${target.web_inf}" mergedir="${merge.dir}">
      <fileset dir="${src.dir}">
        <include name="**/*.java"/>
      </fileset>
<springxml destinationFile="bbs-servlet.xml" \
    xmlencoding="${local.encoding}"/>
    </springdoclet>
    <copy todir="{$src.web_inf}" filtering="no" overwrite="yes">
      <fileset dir="${target.web_inf}" includes="bbs-servlet.xml"/>
    </copy>
  </target>

	    

とし、僕の場合は

  <target name="native2ascii" depends="spring">

	    

としたので、native2asciiが行われる直前にbbs-servlet.xmlというファイル名にてbeanの定義等が書き出される設定にしました。

次はSpringでStruts

SpringとStrutsの連携には3つの方法が用意されています。ここにそれぞれに説明がありますが、今回は「DelegatingRequestProcessor」を使う方法で行くことにしました。さて、そのためにはStrutsのコントローラーを「DelegatingRequestProcessor」に変更する必要があるので、いつもの通り、mergeディレクトリーにstruts-controller.xmlというファイルを作成し、

<controller
processorClass="org.springframework.web.struts.DelegatingRequestProcessor"
/>

	    

と書き込みます。その上で、struts-config.xmlとSpringのbbs-servlet.xmlの両方にactionの定義を書き込むと、DIができるようになります。例えば、

package com.chikkun.jbbs;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.chikkun.jbbs.database.BBS;
import com.chikkun.jbbs.database.BBSDAO;
import com.chikkun.jbbs.database.IBBSDAO;

/**
 * @struts.action name="newContributionForm" path="/newContribution"
* scope="request" validate="true" input="/WEB-INF/template/contribution.vm"
 * @struts.action-forward name="success" path="/WEB-INF/template/tmp.vm"
 * @spring.bean name="/newContribution"
 * @spring.property name="bbs" ref="bbs"
 * @spring.property name="dao" ref="bdao"
 * @author chikkun
 */
public class NewContributionAction extends Action {

    private IBBSDAO dao;
    private BBS bbs;
    /**
     * Performs the simple action
     * 
     * @param mapping
* the action mappings where you find the return value of the
     *            forward
     * @param actionForm
* the action form used, in this example it will be an instance
     *            of the DynaActionForm class
     * @param request
     *            the HTTP servlet request
     * @param response
     *            the HTTP servlet response
     */
public ActionForward execute(ActionMapping mapping, ActionForm actionForm,
            HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
NewContributionActionForm form = (NewContributionActionForm) actionForm;
        
        try {
            BeanUtils.copyProperties(bbs, form);
        } catch (IllegalAccessException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
        bbs.setParent(0);
        bbs.setRegistDate(new Date());
        bbs.setDeleteFlag(new Integer(0));
        dao.save(bbs);
        return (mapping.findForward("success"));
    }
    /**
     * @return bbs
     */
    public final BBS getBbs() {
        return bbs;
    }
    /**
     * @return dao
     */
    public final IBBSDAO getDao() {
        return dao;
    }
    /**
     * @param bbs 設定する bbs
     */
    public final void setBbs(BBS bbs) {
        this.bbs = bbs;
    }
    /**
     * @param dao 設定する dao
     */
    public final void setDao(IBBSDAO dao) {
        this.dao = dao;
    }
}

	    

のように、DAOやBBSなどをセッターインジェクションでActionのインスタンスをもらえます。つまり、

 * @spring.bean name="/newContribution"
 * @spring.property name="bbs" ref="bbs"
 * @spring.property name="dao" ref="bdao"

	   

で、

  <bean
      name="/newContribution"
      class="com.chikkun.jbbs.NewContributionAction"
  >

    <property name="bbs">
      <ref bean="bbs"/>
    </property>
    <property name="dao">
      <ref bean="bdao"/>
    </property>
  </bean>

	   

と書き出され、これによりNewContributionActionがSetter Injectionされるわけです。

ただ、DIができるのはどうもActionであって、ActionFormは出来ないようです(単に調べて切れていないだけかもしれません)。なので、手動でもspringからbeanをもらえるようにします。そのためにはlistenerに登録する必要があるので、mergeディレクトリーにlisteners.xmlというファイルを作成し、

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/bbs-servlet.xml,/WEB-INF/applicationContext.xml</param-value>
</context-param>

	   

と書き込みます。そうするとServletContextを使って、ApplicationContextを取得し、これからbeanをもらいます。具体的には、例えば、

        ServletContext sc = getServlet().getServletContext();
ApplicationContext context = \
    WebApplicationContextUtils.getWebApplicationContext(sc);
        config = (Config) context.getBean("config");

	   

という感じで、Configオブジェクトをもらったり出来ます。ちなみに、このConfigオブジェクトの定義は

  <bean
      id="config"
      class="com.chikkun.jbbs.Config"
  >

    <constructor-arg>
      <value>C:/Tomcat 6.0/webapps/JBBS/WEB-INF/bbs.yml</value>
    </constructor-arg>

  </bean>

	   

と定義されており、これはコンストラクター・インジェクションになっています。

[2007-07-25 14:44]

Acegi Securityとその周辺

認証として、Acegi Securityを使おうと思うのですが、その前に色々準備をしておかなくてはなりません。

DBのtable作成

とりあえず、Acegiで利用するDBを用意しようと思うのですが、以前使っていたDTOに対するUsersクラスではちょっと単純なので、今度は逆にDTOを作成して、それからテーブル作成のSQL文(schema)を作成するという方向でいってみます。

ただし、Acegiでは

	    SELECT username, password, enabled from users WHERE username = ?
	  

	    SELECT username, authority from authorities WHERE username = ?
	  

で、ユーザー名(ログイン名)とパスワード、有効性(bolean)、さらにRole(Authority)が取得できれば自分では認証等のクラスを作成する必要がないようなので、これに合わせようと思います。

ただ、「org.acegisecurity.userdetails.User」というクラスがあるので、これをextendsしてDTOにしようと思ったのですが、どうも「引数のないコンストラクター」が用意されておらず、これではHibernateでは使えません。なので、ちょっと面倒ですが、Acegi用のUserクラスはそのまま使い、Hibernate用のUser(DTO)を作成します。

DTOやDAO

こんな感じ
  • User.java

    USERSテーブルのDTO

      1: 
      2: 		
      3: 		package com.chikkun.common.login.database;
      4: 
      5: import java.io.Serializable;
      6: import java.util.Set;
      7: /**
      8:  * @author chikkun
      9:  * @version 0.1
     10:  * @hibernate.class table="USERS"
     11:  */
     12: public class User implements Serializable {
     13: 
     14:     /**
     15:      * serialVersionUID
     16:      */
     17:     private static final long serialVersionUID = 6744895361156550370L;
     18: 
     19:     /**
     20:      * ユーザーが有効かどうか
     21:      */
     22:     private Boolean enabled = Boolean.TRUE;
     23:     
     24:     /**
     25:      * loggedIn
     26:      */
     27:     private boolean loggedIn = false;
     28:     /**
     29:      * スタッフId
     30:      */
     31:     private Integer id;
     32: 
     33:     /**
     34:      * スタッフログイン名
     35:      */
     36:     private String username;
     37:     /**
     38:      * スタッフパスワード
     39:      */
     40:     private String password;
     41:     /**
     42:      * スタッフメールアドレス
     43:      */
     44:     private String email;
     45:     /**
     46:      * スタッフ備考
     47:      */
     48:     private String note;
     49:     /**
     50:      * スタッフ氏名(ログイン名---usernameと区別すること)
     51:      */
     52:     private String name;
     53:     /**
     54:      * スタッフ氏名読み
     55:      */
     56:     private String nameRead;
     57:     /**
     58:      * 役割(子供)
     59:      */
     60:     private Set userRole;
     61: 
     62:     /**
     63:      * デフォルトコンストラクタ
     64:      */
     65:     public User() {
     66:     }
     67: 
     68:     /**
     69:      * @return ユーザーのID
     70:      * @hibernate.id
     71:      *   name="id"
     72:      *   column="ID"
     73:      *   type="java.lang.Integer"
     74:      *   unsaved-value="null"
     75:      *   generator-class="sequence"
     76:      * @hibernate.generator-param
     77:      *   name="sequence"
     78:      *   value="SEQ_USER"
     79:      */
     80:     public Integer getId() {
     81:         return id;
     82:     }
     83:     /**
     84:      * @param temp
     85:      * スタッフID
     86:      */
     87:     public void setId(final Integer temp) {
     88:         this.id = temp;
     89:     }
     90:     /**
     91:      * @return スタッフログイン名
     92:      * @hibernate.property
     93:      *   column="USERNAME"
     94:      *   not-null="true"
     95:      *   unique="true"
     96:      *   length="64"
     97:      */
     98:     public String getUsername() {
     99:         return username;
    100:     }
    101:     /**
    102:      * @param loginName_
    103:      * username
    104:      */
    105:     public void setUsername(final String loginName_) {
    106:         this.username = loginName_;
    107:     }
    108:     /**
    109:      * @return スタッフパスワード
    110:      * @hibernate.property
    111:      *   column="PASSWORD"
    112:      *   not-null="true"
    113:      *   length="64"
    114:      */
    115:     public String getPassword() {
    116:         return password;
    117:     }
    118:     /**
    119:      * @param pass_
    120:      * string
    121:      */
    122:     public void setPassword(final String pass_) {
    123:         this.password = pass_;
    124:     }
    125:     /**
    126:      * @return スタッフメールアドレス
    127:      * @hibernate.property
    128:      *   column="EMAIL"
    129:      *   not-null="false"
    130:      *   length="64"
    131:      */
    132:     public String getEmail() {
    133:         return email;
    134:     }
    135:     /**
    136:      * @param email_
    137:      * スタッフメールアドレス
    138:      */
    139:     public void setEmail(final String email_) {
    140:         this.email = email_;
    141:     }
    142:     /**
    143:      * @return スタッフ備考
    144:      * @hibernate.property
    145:      *   column="NOTE"
    146:      *   not-null="false"
    147:      *   length="256"
    148:      */
    149:     public String getNote() {
    150:         return note;
    151:     }
    152:     /**
    153:      * @param note_
    154:      * スタッフ備考
    155:      */
    156:     public void setNote(final String note_) {
    157:         this.note = note_;
    158:     }
    159:     /**
    160:      * @return スタッフ氏名
    161:      * @hibernate.property
    162:      *   column="NAME"
    163:      *   not-null="false"
    164:      *   length="64"
    165:      */
    166:     public String getName() {
    167:         return name;
    168:     }
    169:     /**
    170:      * @param name_
    171:      * スタッフ氏名
    172:      */
    173:     public void setName(final String name_) {
    174:         this.name = name_;
    175:     }
    176:     /**
    177:      * @return スタッフ氏名読み
    178:      * @hibernate.property
    179:      *   name="nameRead"
    180:      *   column="NAME_READ"
    181:      *   not-null="false"
    182:      *   length="64"
    183:      */
    184:     public String getNameRead() {
    185:         return nameRead;
    186:     }
    187:     /**
    188:      * @param nameRead_
    189:      * スタッフ氏名読み
    190:      */
    191:     public void setNameRead(final String nameRead_) {
    192:         this.nameRead = nameRead_;
    193:     }
    194:     /**
    195:      * @return UserRole
    196:      * @hibernate.set lazy="true" inverse="false" cascade="all"
    197:      * @hibernate.key column="USER_ID"
    198:      * @hibernate.one-to-many class="com.chikkun.common.login.database.UserRole"
    199:      */
    200:     public Set getUserRole () {
    201:         return userRole;
    202:     }
    203: 
    204:     /**
    205:      * getter.
    206:      * @return enabled を戻します.
    207:      * @hibernate.property
    208:      *   column="ENABLED"
    209:      *   not-null="true"
    210:      */
    211:     public final Boolean getEnabled() {
    212:       return this.enabled;
    213:     }
    214:     
    215:     
    216:     /**
    217:      * @param userRole_
    218:      * UserRole
    219:      *
    220:      */
    221:     public final void setUserRole(final Set userRole_) {
    222:         this.userRole = userRole_;
    223:     }
    224:     
    225:     /**
    226:      * @return loggedIn を戻します.
    227:      */
    228:     public final boolean isLoggedIn() {
    229:         return this.loggedIn;
    230:     }
    231:     /**
    232:      * @param loggedIn_ loggedIn を設定.
    233:      */
    234:     public final void setLoggedIn(final boolean loggedIn_) {
    235:         this.loggedIn = loggedIn_;
    236:     }
    237: 
    238:    /**
    239:      * setter.
    240:      * @param _enabled enabled を設定.
    241:      */
    242:     public final void setEnabled(Boolean _enabled) {
    243:       this.enabled = _enabled;
    244:     }
    245:     
    246: }
    
  • IUserDAO.java

    USERSテーブルのDAOのInterface

     1: 
     2: 		
     3: 		/*
     4:  * 作成日: 2005/07/02
     5:  *
     6:  */
     7: package com.chikkun.common.login.database;
     8: 
     9: import java.util.Collection;
    10: import java.util.List;
    11: 
    12: /**
    13:  * @author Chiku Kazuro userDAOのインターフェース.
    14:  */
    15: public interface IUserDAO {
    16: 
    17:     /**
    18:      * @param _user
    19:      *            保存
    20:      */
    21:     public void save(User _User);
    22: 
    23:     public void saveOrUpdate(User _user);
    24: 
    25:     /**
    26:      * @param _user
    27:      *            更新
    28:      */
    29:     public void update(User _user);
    30: 
    31:     /**
    32:      * @param _user
    33:      *            削除
    34:      */
    35:     public void delete(User _user);
    36: 
    37:     /**
    38:      * @param _id
    39:      * @return List 実際には1つのList.
    40:      */
    41:     public List findById(Integer _id);
    42: 
    43:     /**
    44:      * @return List 全データ
    45:      */
    46:     public List findAll();
    47: 
    48:     /**
    49:      * @param _username
    50:      * @return List loginNameで検索した結果(実際には1つ)
    51:      */
    52:     public List findByUsername(String _username);
    53: 
    54:     /**
    55:      * @param _loginId
    56:      * @return boolean そのloginNameがいたらtrueを返す.
    57:      */
    58:     public boolean isExistUsername(String _username);
    59: 
    60:     /**
    61:      * @param _user
    62:      * @return boolean そのUserと同じではない(equals)Userがいたらtrueを返す。<br>
    63:      *         updateでusernameが変更された場合に使用.
    64:      */
    65:     public boolean isExistUsername(User _user);
    66: 
    67:     /**
    68:      * UsersとAuthorities(User_role)を全部殺す
    69:      */
    70:     public void deleteAll(Collection all);
    71: }
    
  • UserDAO.java

    USERSテーブルのDAOの実装

      1: 
      2: 		
      3: 		/*
      4:  * 作成日: 2007/08/12
      5:  *
      6:  */
      7: package com.chikkun.common.login.database;
      8: 
      9: import java.util.Collection;
     10: import java.util.Iterator;
     11: import java.util.List;
     12: import java.util.Set;
     13: 
     14: import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
     15: 
     16: /**
     17:  * @author chikkun
     18:  * @spring.bean id="udao"
     19:  * @spring.property name="sessionFactory" ref="sessionFactory"
     20:  */
     21: public class UserDAO extends HibernateDaoSupport implements IUserDAO {
     22: 
     23:     /**
     24:      * @param _user
     25:      *            親(User)のBeanを受け取ってインサート。
     26:      */
     27:     public final void save(final User _user) {
     28:         getHibernateTemplate().save(_user);
     29:     }
     30: 
     31:     /**
     32:      * @param _user
     33:      *            Userインスタンスをもらって、すでにデータがあれば更新、なければ保存。
     34:      */
     35:     public final void saveOrUpdate(final User _user) {
     36:         getHibernateTemplate().saveOrUpdate(_user);
     37:     }
     38: 
     39:     /**
     40:      * @param _user
     41:      *            親(User)のBeanを受け取って、それで更新。
     42:      */
     43:     public final void update(final User _user) {
     44:         getHibernateTemplate().update(_user);
     45:     }
     46: 
     47:     /**
     48:      * @param _user
     49:      *            親(User)のBeanを受け取って、それに該当する物を削除。
     50:      *            同時に、それに対応するUserRole(複数の可能性有り)も削除。
     51:      */
     52:     public final void delete(final User _user) {
     53:         Set sr = _user.getUserRole();
     54:         Iterator it = sr.iterator();
     55:         while (it.hasNext()) {
     56:             UserRole rs = (UserRole) it.next();
     57:             getHibernateTemplate().delete(rs);
     58:         }
     59:         getHibernateTemplate().delete(_user);
     60:     }
     61: 
     62:     /**
     63:      * @param id
     64:      *            Integerで受け取る.
     65:      * @return List idで検索して、UserをListで返す(といっても、実際には常に1つ).
     66:      */
     67:     public final List findById(final Integer id) {
     68:         List result = null;
     69:         result = getHibernateTemplate().find(
     70:                 "from User as user where user.id = ?", id);
     71:         return result;
     72:     }
     73: 
     74:     /**
     75:      * @return list 見つかった親のlist
     76:      */
     77:     public final List findAll() {
     78:         return getHibernateTemplate().loadAll(User.class);
     79:     }
     80: 
     81:     /**
     82:      * @param _userName
     83:      *            設定.
     84:      * @return list usernameで検索(実際には1つorなし)
     85:      */
     86:     public List findByUsername(String _userName) {
     87:         List result = null;
     88:         result = getHibernateTemplate().find(
     89:                 "from User as user where user.username = ?", _userName);
     90:         return result;
     91:     }
     92: 
     93:     /**
     94:      * @param loginId
     95:      * @return true or false(反対に考えることも多いので注意!)
     96:      */
     97:     public boolean isExistUsername(final String _username) {
     98:         List list = this.findByUsername(_username);
     99: 
    100:         boolean flag = true;
    101:         if (list.size() == 0) {
    102:             flag = false;
    103:         }
    104:         return flag;
    105:     }
    106: 
    107:     /**
    108:      * a
    109:      */
    110:     public boolean isExistUsername(final User _user) {
    111:         List list = this.findByUsername(_user.getUsername());
    112: 
    113:         boolean flag = true;
    114:         if (list.size() == 0) {
    115:             flag = false;
    116:         } else {
    117:             // 自分だったらOK
    118:             if (_user.equals((User) list.get(0))) {
    119:                 flag = false;
    120:             }
    121:         }
    122:         return flag;
    123:     }
    124:     
    125:     public void deleteAll(Collection all){
    126:         getHibernateTemplate().deleteAll(all);
    127:     }
    128: 
    129: }
    
  • User.java

    AuthoritiesテーブルのDTO

      1: 
      2: 		
      3: 		package com.chikkun.common.login.database;
      4: 
      5: 
      6: import java.io.Serializable;
      7: 
      8: /**
      9:  * @author chikkun
     10:  * Userの子 (User)1:多(UserRole)
     11:  * @hibernate.class table="AUTHORITIES"
     12:  */
     13: public class UserRole implements Serializable {
     14: 
     15:     /**
     16:      * serialVersionUID
     17:      */
     18:     private static final long serialVersionUID = -5116690278522098148L;
     19: 
     20:     /**
     21:      * スタッフロールId
     22:      */
     23:     private Integer id;
     24:     /**
     25:      * スタッフログイン名
     26:      */
     27:     private String username;
     28:     /**
     29:      * スタッフロールズ名
     30:      */
     31:     private String roleName;
     32:     /**
     33:      * スタッフ(親)
     34:      */
     35:     private User user;
     36: 
     37:     /**
     38:      * デフォルトコンストラクタ
     39:      */
     40:     public UserRole() {
     41:     }
     42: 
     43:     /**
     44:      * @return id
     45:      * @hibernate.id
     46:      *   column="ID"
     47:      *   name="id"
     48:      *   type="java.lang.Integer"
     49:      *   unsaved-value="null"
     50:      *   generator-class="sequence"
     51:      * @hibernate.generator-param
     52:      *   name="sequence"
     53:      *   value="SEQ_USER_ROLE"
     54:      */
     55:     public Integer getId() {
     56:         return id;
     57:     }
     58: 
     59:     /**
     60:      * @param id_ id 設定.
     61:      */
     62:     public void setId(final Integer id_) {
     63:         this.id = id_;
     64:     }
     65: 
     66:     /**
     67:      * @return username
     68:      * @hibernate.property
     69:      *   column="USERNAME"
     70:      *   not-null="true"
     71:      *   length="64"
     72:      */
     73:     public String getUsername() {
     74:         return username;
     75:     }
     76: 
     77:     /**
     78:      * @param _username _username 設定.
     79:      */
     80:     public void setUsername(final String _username) {
     81:         this.username = _username;
     82:     }
     83: 
     84:     /**
     85:      * @return roleName
     86:      * @hibernate.property
     87:      *   column="AUTHORITY"
     88:      *   not-null="true"
     89:      *   length="64"
     90:      */
     91:     public String getRoleName() {
     92:         return roleName;
     93:     }
     94: 
     95:     /**
     96:      * @param roleName_ roleName 設定.
     97:      */
     98:     public void setRoleName(final String roleName_) {
     99:         this.roleName = roleName_;
    100:     }
    101: 
    102:     /**
    103:      * @return user
    104:      * @hibernate.many-to-one
    105:      *   column="USER_ID" lazy="false"
    106:      *   class="com.chikkun.common.login.database.User"
    107:      */
    108:     public User getUser() {
    109:         return this.user;
    110:     }
    111: 
    112:     /**
    113:      * @param _user user 設定.
    114:      */
    115:     public void setUser(final User _user) {
    116:         this.user = _user;
    117:     }
    118: }
    
  • IUserDAO.java

    AuthoritiesテーブルのDAOのInterface

     1: 
     2: 		
     3: 		/*
     4:  * 作成日: 2007/08/12
     5:  *
     6:  */
     7: package com.chikkun.common.login.database;
     8: 
     9: import java.util.Collection;
    10: import java.util.List;
    11: 
    12: /**
    13:  * @author chikkun UserRoleのインターフェース
    14:  */
    15: public interface IUserRoleDAO {
    16: 
    17:     /**
    18:      * @param _userRole
    19:      * 保存
    20:      */
    21:     public abstract void save(final UserRole _userRole);
    22: 
    23:     /**
    24:      * @param _userRole
    25:      * データがあれば更新、なければ保存
    26:      */
    27:     public void saveOrUpdate(final UserRole _userRole);
    28: 
    29:     /**
    30:      * @param _userRole
    31:      * 更新
    32:      */
    33:     public abstract void update(final UserRole _userRole);
    34: 
    35:     /**
    36:      * @param _userRole
    37:      * 削除
    38:      */
    39:     public abstract void delete(final UserRole _userRole);
    40: 
    41:     /**
    42:      * @param _id
    43:      * @return
    44:      */
    45:     public abstract List findById(final Integer _id);
    46: 
    47:     /**
    48:      * @param _username
    49:      * @return
    50:      */
    51:     public abstract List findByUsername(final String _username);
    52: 
    53:     /**
    54:      * @return
    55:      */
    56:     public abstract List findAll();
    57: 
    58:     public void deleteAll(Collection all);
    59: 
    60: }
    
  • UserDAO.java

    AuthoritiesテーブルのDAOの実装

     1: 
     2: 		
     3: 		/*
     4:  * 作成日: 2007/08/12
     5:  *
     6:  */
     7: package com.chikkun.common.login.database;
     8: 
     9: import java.util.Collection;
    10: import java.util.List;
    11: import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
    12: 
    13: /**
    14:  * @author chikkun
    15:  * @spring.bean id="urdao"
    16:  * @spring.property name="sessionFactory" ref="sessionFactory"
    17:  */
    18: public class UserRoleDAO extends HibernateDaoSupport implements IUserRoleDAO {
    19: 
    20:     /**
    21:      * @param _userRole
    22:      *            UserRoleインスタンスを受け取って、保存。
    23:      */
    24:     public final void save(final UserRole _userRole) {
    25:         getHibernateTemplate().save(_userRole);
    26:     }
    27: 
    28:     /**
    29:      * @param _userRole
    30:      *            UserRoleインスタンスを受け取って、存在していれば更新、そうでなければ更新。
    31:      */
    32:     public final void saveOrUpdate(final UserRole _userRole) {
    33:         getHibernateTemplate().saveOrUpdate(_userRole);
    34:     }
    35: 
    36:     /**
    37:      * @param _userRole
    38:      *            UserRoleのインスタンスを受け取って、更新。
    39:      */
    40:     public final void update(final UserRole _userRole) {
    41:         getHibernateTemplate().update(_userRole);
    42:     }
    43: 
    44:     /**
    45:      * @param _userRole
    46:      *            UserRoleのインスタンスを受け取って、対応するレコードを削除。
    47:      */
    48:     public final void delete(final UserRole _userRole) {
    49:         getHibernateTemplate().delete(_userRole);
    50:     }
    51: 
    52:     /**
    53:      * @param id
    54:      *            findById Integerで受け取る.
    55:      * @return List idで検索して、UserRoleをListで返す(といっても、実際には常に1つ).
    56:      */
    57:     public final List findById(final Integer id) {
    58:         List result = null;
    59:         result = getHibernateTemplate().find(
    60:                 "from UserRole as staffRole where staffRole.id = ?", id);
    61:         return result;
    62:     }
    63: 
    64:     /**
    65:      * @param _userName
    66:      *            設定.
    67:      * @return list usernameで検索(存在する場合は、1つ以上)
    68:      */
    69:     public List findByUsername(String _userName) {
    70:         List result = null;
    71:         result = getHibernateTemplate().find(
    72:                 "from UserRole as ur where ur.username = ?", _userName);
    73:         return result;
    74:     }
    75: 
    76:     /**
    77:      * @return list 全てのレコードをフェッチ。
    78:      */
    79:     public final List findAll() {
    80:         return getHibernateTemplate().loadAll(UserRole.class);
    81: 
    82:     }
    83:     public void deleteAll(Collection all){
    84:         getHibernateTemplate().deleteAll(all);
    85:     }
    86: }
    
  • Role.java

    RolesテーブルのDTO

      1: 
      2: 		
      3: 		/*
      4:  * Created on 2007/08/12
      5:  * @author chikkun
      6:  */
      7: 
      8: package com.chikkun.common.login.database;
      9: 
     10: 
     11: 
     12: import java.io.Serializable;
     13: 
     14: /**
     15:  * @author chikkun
     16:  *   StaffRoleのユーティリティ的なクラス。
     17:  *   ここに記載されたものがHTMLセレクトで使用される.
     18:  * @hibernate.class table="ROLES"
     19:  */
     20: public class Role implements Serializable {
     21: 
     22:     /**
     23:      * serialVersionUID
     24:      */
     25:     static final long serialVersionUID = -1016766824498159290L;
     26: 
     27:     /**
     28:      * スタッフロールズID
     29:      */
     30:     private Integer id;
     31: 
     32:     /**
     33:      * スタッフロールズ名
     34:      */
     35:     private String roleName;
     36: 
     37:     /**
     38:      * <code>roleDisplayName</code> 表示用の役割名
     39:      */
     40:     private String roleDisplayName;
     41: 
     42:     /**
     43:      * @param roleName
     44:      *   フルコンストラクタ
     45:      */
     46:     public Role(final String _roleName) {
     47:         this.roleName = _roleName;
     48:     }
     49: 
     50:     /**
     51:      * デフォルトコンストラクタ
     52:      */
     53:     public Role() {
     54:     }
     55: 
     56:     /**
     57:      * @return id 戻します.
     58:      * @hibernate.id
     59:      *   column="ID"
     60:      *   name="id"
     61:      *   type="java.lang.Integer"
     62:      *   unsaved-value="null"
     63:      *   generator-class="sequence"
     64:      * @hibernate.generator-param
     65:      *   name="sequence"
     66:      *   value="SEQ_ROLE"
     67:      */
     68:     public Integer getId() {
     69:         return this.id;
     70:     }
     71: 
     72:     /**
     73:      * @param id_
     74:      * ロールズID
     75:      */
     76:     public void setId(final Integer id_) {
     77:         this.id = id_;
     78:     }
     79: 
     80:     /**
     81:      * @return RoleName 戻します.
     82:      * @hibernate.property
     83:      *   name="roleName"
     84:      *   column="ROLE_NAME"
     85:      *   not-null="true"
     86:      *   unique="true"
     87:      *   length="64"
     88:      */
     89:     public String getRoleName() {
     90:         return this.roleName;
     91:     }
     92: 
     93:     /**
     94:      * @param roleName_
     95:      * RoleName を設定.
     96:      */
     97:     public void setRoleName(final String roleName_) {
     98:         this.roleName = roleName_;
     99:     }
    100: 
    101:     /**
    102:      * @return roleDisplayName 戻します.
    103:      * @hibernate.property
    104:      *   name="roleName"
    105:      *   column="DISPLAY_NAME"
    106:      *   not-null="false"
    107:      *   unique="true"
    108:      *   length="64"
    109:      */
    110:     public String getRoleDisplayName() {
    111:         return this.roleDisplayName;
    112:     }
    113: 
    114:     /**
    115:      * @param roleDisplayName_
    116:      * roleDisplayName を設定.
    117:      */
    118:     public void setRoleDisplayName(final String roleDisplayName_) {
    119:         this.roleDisplayName = roleDisplayName_;
    120:     }
    121: }
    
  • IUserDAO.java

    RolesテーブルのDAOのInterface

     1: 
     2: 		
     3: 		/*
     4:  * 作成日: 2007/08/12
     5:  *
     6:  */
     7: package com.chikkun.common.login.database;
     8: 
     9: import java.util.Collection;
    10: import java.util.List;
    11: 
    12: /**
    13:  * @author chikkun
    14:  * RoleDAOのインターフェース
    15:  */
    16: public interface IRoleDAO {
    17:     /**
    18:      * @param role
    19:      * 保存
    20:      */
    21:     public abstract void save(final Role role);
    22: 
    23:     /**
    24:      * @param role
    25:      * データがあれば更新、なければ保存
    26:      */
    27:     public void saveOrUpdate(final Role role);
    28: 
    29:     /**
    30:      * @param role
    31:      * 更新
    32:      */
    33:     public void update(final Role role);
    34: 
    35:     /**
    36:      * @param role
    37:      * 削除
    38:      */
    39:     public void delete(final Role role);
    40: 
    41:     /**
    42:      * @param _roleName
    43:      * @return boolean そのroleNameがすでに登録されているとtrueを返す
    44:      */
    45:     public boolean isExistRoleName(String _roleName);
    46: 
    47:     /**
    48:      * @param _id
    49:      * @return List 実際には1つのList.
    50:      */
    51:     public List findById(Integer _id);
    52: 
    53:     /**
    54:      * @return list 全部フェッチ.
    55:      */
    56:     public abstract List findAll();
    57:     
    58:     public abstract void deleteAll(Collection all);
    59: }
    
  • UserDAO.java

    RolesテーブルのDAOの実装

     1: 
     2: 		
     3: 		/*
     4:  * 作成日: 2007/08/12
     5:  *
     6:  */
     7: package com.chikkun.common.login.database;
     8: 
     9: import java.util.Collection;
    10: import java.util.List;
    11: 
    12: import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
    13: 
    14: /**
    15:  * @author Chiku Kazuro
    16:  * @version 0.1 Rolesテーブルを操作するDAO。
    17:  * @spring.bean id="rdao"
    18:  * @spring.property name="sessionFactory" ref="sessionFactory"
    19:  */
    20: public class RoleDAO extends HibernateDaoSupport implements IRoleDAO {
    21: 
    22:     public RoleDAO() {
    23:     }
    24: 
    25:     /**
    26:      * @param _role
    27:      * 保存
    28:      */
    29:     public final void save(final Role _role) {
    30:         getHibernateTemplate().save(_role);
    31:     }
    32: 
    33:     /**
    34:      * @param _role
    35:      * 更新
    36:      */
    37:     public final void update(final Role _role) {
    38:         getHibernateTemplate().update(_role);
    39:     }
    40: 
    41:     /**
    42:      * @param _role
    43:      * Roleインスタンスに該当するレコードを削除。
    44:      */
    45:     public final void delete(final Role _role) {
    46:         getHibernateTemplate().delete(_role);
    47:     }
    48: 
    49:     /**
    50:      * @param _role
    51:      * すでに登録されていれば更新、そうでなければ保存。
    52:      */
    53:     public final void saveOrUpdate(final Role _role) {
    54:         getHibernateTemplate().saveOrUpdate(_role);
    55:     }
    56: 
    57:     /**
    58:      * @param _roleNae
    59:      * ロール名がすでに登録されている場合は
    60:      * trueを返す。
    61:      */
    62:     public boolean isExistRoleName(String _roleName) {
    63:         List result = null;
    64:         boolean flag = false;
    65:         result = getHibernateTemplate().find(
    66:                 "from Role as role where user.role_name = ?", _roleName);
    67:         if (result != null && result.size() > 0) {
    68:             flag = true;
    69:         }
    70:         return flag;
    71:     }
    72:     /**
    73:      * @param id
    74:      *            Integerで受け取る.
    75:      * @return List idで検索して、UserをListで返す(といっても、実際には常に1つ).
    76:      */
    77:     public final List findById(final Integer id) {
    78:         List result = null;
    79:         result = getHibernateTemplate().find(
    80:                 "from Role as role where role.id = ?", id);
    81:         return result;
    82:     }
    83: 
    84:     /**
    85:      * @return list 全部フェッチ.
    86:      */
    87:     public final List findAll() {
    88:         return getHibernateTemplate().loadAll(Role.class);
    89:     }
    90:     
    91:     public void deleteAll(Collection all){
    92:         getHibernateTemplate().deleteAll(all);
    93:     }
    94: }
    

さて、上記DTOに埋め込まれているXdocletタグより、schemaを作成します。そのためにはbuild.xmlに追加します。

<target name="schemaexport" description="make tables on Database" \
    depends="hibernate">
<taskdef name="schemaexport" \
    classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
              classpathref="build.classpath"/>
<schemaexport config="${resources.dir}/hibernate.cfg.xml" quiet="no" \
    text="no" drop="no"
      output="schema-export.sql" delimiter=";">
      <fileset dir="${src.dir}">
         <include name="**/*.hbm.xml"/>
      </fileset>	
    </schemaexport>
  </target>

	    

これで、出来たのが以下だけれど、sequenceの部分があまり頂けないし(確かHQLDBなどではもっとましだった記憶があるのですけど)、改行がなく読みづらいので、多少修正したのがあとのものです。

alter table AUTHORITIES drop constraint FKAB62A70186D165C3;
drop table AUTHORITIES;
drop table BBS;
drop table ROLES;
drop table USERS;
drop sequence 
            SEQ_BBS
        ;
drop sequence 
            SEQ_ROLE
        ;
drop sequence 
            SEQ_USER
        ;
drop sequence 
            SEQ_USER_ROLE
        ;
create table AUTHORITIES (ID int4 not null, USERNAME varchar(64) not null, \
    AUTHORITY varchar(64) not null, USER_ID int4, primary key (ID));
create table BBS (KEY int4 not null, PARENT int4 not null, TITLE \
    varchar(255) not null, CONTENTS varchar(255) not null, REGIST_DATE \
    timestamp not null, USER_ID varchar(255) not null, URL varchar(255), \
    E_MAIL varchar(255) not null, DELETE_FLAG int4, primary key (KEY));
create table ROLES (ID int4 not null, ROLE_NAME varchar(64) not null \
    unique, DISPLAY_NAME varchar(64) unique, primary key (ID));
create table USERS (ID int4 not null, USERNAME varchar(64) not null unique, \
    PASSWORD varchar(64) not null, EMAIL varchar(64), NOTE varchar(256), \
    NAME varchar(64), NAME_READ varchar(64), ENABLED bool not null, primary \
    key (ID));
alter table AUTHORITIES add constraint FKAB62A70186D165C3 foreign key \
    (USER_ID) references USERS;
create sequence 
            SEQ_BBS
        ;
create sequence 
            SEQ_ROLE
        ;
create sequence 
            SEQ_USER
        ;
create sequence 
            SEQ_USER_ROLE
        ;

	    

alter table AUTHORITIES drop constraint FKAB62A70186D165C3;

DROP TABLE BBS;
DROP TABLE USERS;
drop table AUTHORITIES;
drop table ROLES;


DROP SEQUENCE SEQ_BBS;
drop sequence SEQ_ROLE;
drop sequence SEQ_USER;
drop sequence SEQ_USER_ROLE;

CREATE SEQUENCE SEQ_BBS;
CREATE SEQUENCE SEQ_USER;
create sequence SEQ_ROLE;
create sequence SEQ_USER_ROLE;


create table AUTHORITIES (
       ID INTEGER DEFAULT nextval('SEQ_USER_ROLE'::text) NOT NULL
     , USERNAME varchar(64) not null
     , AUTHORITY varchar(64) not null
     , USER_ID int4
     , primary key (ID)
);

create table USERS (
       ID INTEGER DEFAULT nextval('SEQ_USER'::text) NOT NULL
     , USERNAME varchar(64) not null unique
     , PASSWORD varchar(64) not null
     , EMAIL varchar(64)
     , NOTE varchar(256)
     , NAME varchar(64)
     , NAME_READ varchar(64)
     , ENABLED bool not null
     , primary key (ID)
);


create table ROLES (
       ID INTEGER DEFAULT nextval('SEQ_ROLE'::text) NOT NULL
     , ROLE_NAME varchar(64) not null unique
     , DISPLAY_NAME varchar(64) unique
     , primary key (ID)
);

CREATE TABLE BBS (
       ID INTEGER DEFAULT nextval('SEQ_BBS'::text) NOT NULL
     , PARENT INTEGER NOT NULL
     , TITLE VARCHAR(256) NOT NULL
     , CONTENTS TEXT NOT NULL
     , REGIST_DATE TIMESTAMP NOT NULL 
     , USER_ID VARCHAR(256)
     , URL VARCHAR(512)
     , E_MAIL VARCHAR(512)
     , DELETE_FLAG INTEGER
     , PRIMARY KEY (ID)
);

alter table AUTHORITIES add constraint FKAB62A70186D165C3 foreign key \
    (USER_ID) references USERS;


	    

として、別のconf/bbs.sqlあたりに保存しておき、psqlで実行して、テーブルを作成します。

また、DAOはspringに登録したいし、sessionFactoryをセッターインジェクションしたいので、BBSDAOの場合と同様(上ですでに引用しているように)、

/**
 * @author chikkun
 * @spring.bean id="udao"
 * @spring.property name="sessionFactory" ref="sessionFactory"
 */
public class UserDAO extends HibernateDaoSupport implements IUserDAO {

	    

というように、それぞれsessionFactoryをセッターインジェクションするようにします(その他のDAOも同様に)。

User関係をしきるクラス作成(SpringでTransactionも!)

以前あったUMLのクラス図にもあったように、UserDataUtils.javaクラスを作成しますが、次のようなSpringのBean設定にします(色々書き込んでありますが、マニュアルでも見ながらじっくり読んでみてください)。AOP等も駆使して、ログを吐いています。

    <bean id="userTransactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
        <property name="transactionAttributeSource">
            <value>
com.chikkun.common.login.database.UserDataUtils.save*=PROPAGATION_REQUIRED,-UserAlreadyExistException
com.chikkun.common.login.database.UserDataUtils.update*=PROPAGATION_REQUIRED
com.chikkun.common.login.database.UserDataUtils.delete*=PROPAGATION_REQUIRED
            </value>
        </property>
        <!--
        <property name="postInterceptors">
            <list>
                <value>throwsLoggingAdvisor</value>
            </list>
        </property>
        -->
    </bean>
    <bean id="userService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
        <property name="target">
            <ref bean="userDataUtilsTarget"/>
        </property>
        <property name="proxyInterfaces">
            <value>com.chikkun.common.login.database.IUserDataUtils</value>
        </property>
        <property name="transactionAttributes">
            <props>
                <prop key="save*">PROPAGATION_REQUIRED</prop>
                <prop key="update*">PROPAGATION_REQUIRED</prop>
                <prop key="delete*">PROPAGATION_REQUIRED</prop>
                <prop key="find*">PROPAGATION_REQUIRED, readOnly</prop>
            </props>
        </property>
    </bean>
<bean id="userDataUtilsTarget" \
    class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
            <ref bean="userDataUtils"/>
        </property>
        <property name="interceptorNames">
            <value>loggingInterceptor</value>
        </property>
    </bean>
<bean id="hibernateInterceptor" \
    class="org.springframework.orm.hibernate3.HibernateInterceptor">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
    <!--  Logging   -->
<bean id="throwsLoggingAdvice" \
    class="com.chikkun.jbbs.database.ThrowsLoggingAdvice"/>
<bean id="loggingInterceptor" \
    class="com.chikkun.common.LoggingInterceptor"/>

	    

上記の設定等を実現するために、UserDataUtils.javaを実装します。

UsersやAuthoritiesやRolesなどのテーブルを担当するクラス

  1: 
  2: 	    
  3: 	    /*
  4:  * 作成日: 2007/08/13
  5:  */
  6: package com.chikkun.common.login.database;
  7: 
  8: import java.io.Serializable;
  9: import java.util.Collection;
 10: import java.util.HashSet;
 11: import java.util.Iterator;
 12: import java.util.List;
 13: import java.util.Set;
 14: 
 15: import org.springframework.dao.DataIntegrityViolationException;
 16: 
 17: /**
 18:  * @author chikkun
 19:  * @version 0.1 UserとUserRoleを使って登録.
 20:  * @spring.bean id="userDataUtils"
 21:  * @spring.property name="userDAO" ref="udao"
 22:  * @spring.property name="userRoleDAO" ref="urdao"
 23:  * @spring.property name="roleDAO" ref="rdao"
 24:  */
 25: public class UserDataUtils implements Serializable, IUserDataUtils {
 26: 
 27:     /**
 28:      * serialVersionUID
 29:      */
 30:     private static final long serialVersionUID = -8057434389491727402L;
 31: 
 32:     /**
 33:      * <code>udao</code> User用のDAO.
 34:      */
 35:     private IUserDAO userDAO;
 36: 
 37:     private IRoleDAO roleDAO;
 38: 
 39:     /**
 40:      * <code>urdao</code> UserRole用のDAO.
 41:      */
 42:     private IUserRoleDAO userRoleDAO;
 43: 
 44: 
 45:     /**
 46:      * @return roleDAO を戻します.
 47:      */
 48:     public final IRoleDAO getRoleDAO() {
 49:         return this.roleDAO;
 50:     }
 51: 
 52:     /**
 53:      * @param roleDAO_
 54:      *            roleDAO を設定.
 55:      */
 56:     public final void setRoleDAO(IRoleDAO _roleDAO) {
 57:         this.roleDAO = _roleDAO;
 58:     }
 59: 
 60:     /**
 61:      * defaultコンストラクタ.
 62:      */
 63:     public UserDataUtils() {
 64:         super();
 65:     }
 66: 
 67:     /**
 68:      * UserとUserRoleを保存.
 69:      */
 70:     public void save(User _user, List _role) throws UserAlreadyExistException {
 71:         try {
 72:             Set set = new HashSet();
 73:             Iterator it = _role.iterator();
 74:             while(it.hasNext()){
 75:                 UserRole userRole = new UserRole();
 76:                 userRole.setUsername(_user.getUsername());
 77:                 userRole.setRoleName((String)it.next());
 78:                 set.add(userRole);
 79:             }
 80:             _user.setUserRole(set);
 81:             userDAO.save(_user);
 82:         } catch (Exception ex) {
 83:             ex.printStackTrace();
 84:         }
 85:     }
 86: 
 87:     public void update(User _user) {
 88:         userDAO.update(_user);
 89:     }
 90: 
 91:     public void delete(User _user) {
 92:         userDAO.delete(_user);
 93:     }
 94: 
 95:     public void saveRole(Role _role) throws UserAlreadyExistException {
 96:         try {
 97:             roleDAO.save(_role);
 98:         } catch (DataIntegrityViolationException err) {
 99:             throw new UserAlreadyExistException(
100:                     _role.getRoleName() + "は存在します!", err);
101:         }
102: 
103:     }
104: 
105:     /**
106:      * @return userDAO を戻します.
107:      */
108:     public final IUserDAO getUserDAO() {
109:         return this.userDAO;
110:     }
111: 
112:     /**
113:      * @param userDAO_
114:      *            userDAO を設定.
115:      */
116:     public final void setUserDAO(IUserDAO userDAO_) {
117:         this.userDAO = userDAO_;
118:     }
119: 
120:     /**
121:      * @return userRoleDAO を戻します.
122:      */
123:     public final IUserRoleDAO getUserRoleDAO() {
124:         return this.userRoleDAO;
125:     }
126: 
127:     /**
128:      * @param userRoleDAO_
129:      *            userRoleDAO を設定.
130:      */
131:     public final void setUserRoleDAO(IUserRoleDAO userRoleDAO_) {
132:         this.userRoleDAO = userRoleDAO_;
133:     }
134: 
135:     public boolean isExistUsername(String _username) {
136:         return userDAO.isExistUsername(_username);
137:     }
138: 
139:     public boolean isExistUsername(User _user) {
140:         return userDAO.isExistUsername(_user);
141:     }
142: 
143:     public List findUserByUsername(String _username) {
144:         return userDAO.findByUsername(_username);
145: 
146:     }
147: 
148:     public List findUserById(Integer id_) {
149:         return userDAO.findById(id_);
150: 
151:     }
152: 
153:     public void deleteAll() {
154:         Collection allUserRole = this.userRoleDAO.findAll();
155:         Collection allUser = this.userDAO.findAll();
156:         Collection allRole = this.roleDAO.findAll();
157:         this.roleDAO.deleteAll(allRole);
158:         //this.userRoleDAO.deleteAll(allUserRole);
159:         this.userDAO.deleteAll(allUser);
160:     }
161: 
162: }

さらに、次の2つのクラスを使っているので・・・

例外が起こった際のロギング

 1: 
 2: 	    
 3: 	    /*
 4:  * 作成日: 2005/07/03
 5:  *
 6:  */
 7: package com.chikkun.jbbs.database;
 8: 
 9: import java.lang.reflect.Method;
10: 
11: import org.apache.log4j.Logger;
12: import org.springframework.aop.ThrowsAdvice;
13: import org.springframework.dao.DataAccessException;
14: /**
15:  * @author Chiku Kazuro
16:  * @version 0.1
17:  */
18: public class ThrowsLoggingAdvice implements ThrowsAdvice {
19: 
20:   protected static final Logger logger = Logger.getLogger("database");
21: 
22: 
23:   public void afterThrowing(Exception ex){
24:     System.out.println("例外が発生しました!");
25:   }
26: 
27:   public void afterThrowing(Method m, Object[] args, Object target, DataAccessException ex){
28:     //DOMConfigurator.configure("log4j.xml");
29:     StringBuffer sb = new StringBuffer();
30:     sb.append(target.toString());
31:     sb.append(" ");
32:     sb.append(m.getName());
33:     sb.append("メソッドで、");
34:     sb.append(ex.getClass().getName());
35:     sb.append("発生しました!");
36: 
37:     logger.error(sb, ex);
38:   }
39: 
40: }

DBの処理時間のロギング

 1: 
 2: 	    
 3: 	    /*
 4:  * 作成日: 2007/08/13
 5:  *
 6:  */
 7: package com.chikkun.common;
 8: 
 9: import org.aopalliance.intercept.MethodInterceptor;
10: import org.aopalliance.intercept.MethodInvocation;
11: import org.apache.log4j.Logger;
12: /**
13:  * @author Chiku Kazuro
14:  * @version 0.1
15:  */
16: public class LoggingInterceptor implements MethodInterceptor {
17:     protected static final Logger logger = Logger.getLogger("bbs");
18: 
19:     public Object invoke(MethodInvocation invocation) throws Throwable {
20:         //DOMConfigurator.configure("WEB-INF/log4j.xml");
21: 
22:         logger.info("メソッド開始: "
23:                 + invocation.getMethod().getDeclaringClass()
24:                 + "::" + invocation.getMethod().getName());
25: 
26:         long startTime = System.currentTimeMillis();
27: 
28:         try {
29:             Object retVal = invocation.proceed();
30:             return retVal;
31:         } finally {
32:             logger.info("メソッド終了: "
33:                     + invocation.getMethod().getDeclaringClass()
34:                     + "::" + invocation.getMethod().getName());
35:             logger.info("要した時間: "
36:                     + (System.currentTimeMillis() - startTime) + " msec.");
37:         }
38:     }
39: 
40: }

上記のロギングで、log4jが必要になるので、pom.xmlにdependencgyを追加します。

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version> 1.2.14</version>
    </dependency>

	    

ダミーデータの登録

さてさて、これらを使って、ダミーデータを登録してみましょう。

ダミーデータ登録クラス

  1: 
  2: 	    
  3: 	    /*
  4:  * 作成日: 2005/10/11
  5:  */
  6: package com.chikkun.common.login.database;
  7: 
  8: import java.util.ArrayList;
  9: import java.util.List;
 10: 
 11: import org.springframework.beans.factory.BeanFactory;
 12: import org.springframework.context.ApplicationContext;
 13: import org.springframework.context.support.FileSystemXmlApplicationContext;
 14: 
 15: 
 16: public class InsertUserExamples {
 17:   /**
 18:    * @param args
 19:    */
 20:   public static void main(String[] args) {
 21:     IUserDataUtils sdao;
 22:     String[] conf = new String[2];
 23:     conf[0] = "target/JBBS/WEB-INF/applicationContext.xml";
 24:     conf[1] = "target/JBBS/WEB-INF/jbbs-servlet.xml";
 25:     ApplicationContext context = new FileSystemXmlApplicationContext(conf);
 26: 
 27:     BeanFactory beanFactory = (BeanFactory) context;
 28:     sdao = (IUserDataUtils) beanFactory.getBean("userDataUtils");
 29:     sdao.deleteAll();
 30:     Role r1 = new Role();
 31:     r1.setRoleDisplayName("最高責任者");
 32:     r1.setRoleName("ROLE_ADMIN");
 33:     sdao.saveRole(r1);
 34: 
 35:     Role r2 = new Role();
 36:     r2.setRoleDisplayName("マネージャー");
 37:     r2.setRoleName("ROLE_MANAGER");
 38:     sdao.saveRole(r2);
 39: 
 40:     Role r3 = new Role();
 41:     r3.setRoleDisplayName("一般ユーザー");
 42:     r3.setRoleName("ROLE_USER");
 43:     sdao.saveRole(r3);
 44: 
 45:     List allmighty = new ArrayList();
 46:     allmighty.add("ROLE_MANAGER");
 47:     allmighty.add("ROLE_ADMIN");
 48:     allmighty.add("ROLE_USER");
 49: 
 50:     List manager = new ArrayList();
 51:     manager.add("ROLE_MANAGER");
 52: 
 53:     List admin = new ArrayList();
 54:     admin.add("ROLE_ADMIN");
 55: 
 56:     List hira = new ArrayList();
 57:     hira.add("ROLE_USER");
 58: 
 59:     User user = new User();
 60:     user.setName("戸田和彦");
 61:     user.setNameRead("とだかずひこ");
 62:     user.setUsername("toda");
 63:     user.setPassword("ojisan");
 64:     user.setEmail("chikkun@chikkun.com");
 65:     user.setNote("はれほれ");
 66:     sdao.save(user,allmighty);
 67: 
 68:     user = new User();
 69:     user.setName("舛井幸輔");
 70:     user.setNameRead("ますいこうすけ");
 71:     user.setUsername("masuii");
 72:     user.setPassword("kosuke");
 73:     user.setEmail("chikkun@chikkun.com");
 74:     user.setNote("おはようさん");
 75:     sdao.save(user,admin);
 76: 
 77:     user = new User();
 78:     user.setName("知久友子");
 79:     user.setNameRead("ちくともこ");
 80:     user.setUsername("chiku");
 81:     user.setPassword("tomoko");
 82:     user.setEmail("chiku@chikkun.com");
 83:     user.setNote("ふにゃ");
 84:     sdao.save(user,hira);
 85: 
 86:     user = new User();
 87:     user.setName("後藤克博");
 88:     user.setNameRead("ごとうかつひろ");
 89:     user.setUsername("goto");
 90:     user.setPassword("katsuhiro");
 91:     user.setEmail("chikkun@chikkun.com");
 92:     user.setNote("へへへ\nふふふ");
 93:     sdao.save(user,hira);
 94: 
 95:     user = new User();
 96:     user.setName("坂井民雄");
 97:     user.setNameRead("さかいたみお");
 98:     user.setUsername("tamio");
 99:     user.setPassword("sakai");
100:     user.setEmail("chikkun@chikkun.com");
101:     user.setNote("太っちゃった");
102:     sdao.save(user,hira);
103: 
104:     user = new User();
105:     user.setName("岩根");
106:     user.setNameRead("いわね");
107:     user.setUsername("iwane");
108:     user.setPassword("kekkon");
109:     user.setEmail("chikkun@chikkun.com");
110:     user.setNote("やったぜ加藤ちゃん");
111:     sdao.save(user,allmighty);
112: 
113:     user = new User();
114:     user.setName("伝法谷千代春");
115:     user.setNameRead("でんぽうやちよはる");
116:     user.setUsername("denn");
117:     user.setPassword("dennchan");
118:     user.setEmail("chikkun@chikkun.com");
119:     user.setNote("kakakaかかか\nじゃじゃじゃ");
120:     sdao.save(user,manager);
121: 
122:     user = new User();
123:     user.setName("外村");
124:     user.setNameRead("とのむら");
125:     user.setUsername("tonomura");
126:     user.setPassword("tukuba");
127:     user.setEmail("chikkun@chikkun.com");
128:     user.setNote("ウルトラマン");
129:     sdao.save(user,hira);
130: 
131:     user = new User();
132:     user.setName("大内");
133:     user.setNameRead("おおうち");
134:     user.setUsername("ouchi");
135:     user.setPassword("komochi");
136:     user.setEmail("chikkun@chikkun.com");
137:     user.setNote("アメリカ帰り");
138:     sdao.save(user,admin);
139:     
140:     user = new User();
141:     user.setName("メア");
142:     user.setNameRead("めあ");
143:     user.setUsername("meer");
144:     user.setPassword("erisanmeer");
145:     user.setEmail("chikkun@chikkun.com");
146:     user.setNote("よこて");
147:     sdao.save(user,admin);
148:     
149:     user = new User();
150:     user.setName("人々");
151:     user.setNameRead("ひとびと");
152:     user.setUsername("people");
153:     user.setPassword("people");
154:     user.setEmail("chikkun@chikkun.com");
155:     user.setNote("確認用");
156:     sdao.save(user,admin);
157:   }
158: 
159: }

さて、これを実行するには相当classPathが必要で、コマンドラインは実用的じゃないので、build.xmlに次のように書き込み、Antに実行してもらう。

  <target name="insert" description="Data のインサート">
    <java classname="com.chikkun.common.login.database.InsertUserExamples">
      <classpath refid="build.classpath"/>
    </java>
  </target>

	    

クラスパスは現在、JBBS/target/JUBBS/WEB-INF/lib/*jarも入れているので、mvn packageして、しっかりjarファイルをコピーしたり、classファイルをコピーしたりした後でないと、動かないかもしれません。

  <property name="target.web_inf" value="./target/JBBS/WEB-INF"/>
....
  <fileset id="lib.libs" dir="${target.web_inf}">
    <include name="lib/*.jar"/>
  </fileset>
.....
  <path id="build.classpath">
    <pathelement path="${classes.dir}" />
    <fileset refid="struts.libs" />
    <fileset refid="xdoclet.libs" />
    <fileset refid="servlet.jar" />
    <fileset refid="middlegen.libs"/>
    <fileset refid="hsqldb.libs"/>
    <fileset refid="hibernate-tools.libs"/>
    <fileset refid="dom4j.libs"/>
    <fileset refid="log4j.libs"/>
    <fileset refid="dbcp.libs"/>
    <fileset refid="pool.libs"/>
    <fileset refid="lib.libs"/>
  </path>


	    

そうそう、コマンドラインからant insertとすると、実行されるわけですが、もう1つ必要なことがあります。hibernate.cfg.xmlの指定が、applicationContext.xmlの中で

<bean id="sessionFactory" \
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation">
            <value>WEB-INF/classes/hibernate.cfg.xml</value>
        </property>
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
    </bean>

	    

となっており、つまり「WEB-INF/classes/hibernate.cfg.xml」が、実行時には「JBBS/WEB-INF/classes/hibernate.cfg.xml」ということになり、こんなところはないので、ディレクトリーを作成して、hibernate.cfg.xmlコピーしておく必要があります(何かもっとうまい方法ないものだろうか?)。

さあ、これでうまくいけば、あとはAcegiの設定かあ〜。

いよいよAcegiの組み込み

デフォルトなら、ほとんど自分でプログラムを書く必要はありません。まずはweb.xmlにfilterの登録です。

FilterToBeanProxy

まずはweb.xmlへfilterの登録と、filter-mappingの登録です。xdocletのmergeディレクトリーのfilters.xmlに次の文を書き込みます。

<filter>
        <filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
        <init-param>
            <param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
        </init-param>
</filter>

	  

同様に、filter-mappings.xmlは

<filter-mapping>
      <filter-name>Acegi Filter Chain Proxy</filter-name>
      <url-pattern>/*</url-pattern>
</filter-mapping>

	  

となります。

この「org.acegisecurity.util.FilterToBeanProxy」は、init-paramタグにつなげていくフィルターを書いていくような使い方をするのですが、さらに、そのフィルターに「org.acegisecurity.util.FilterChainProxy」を指定し、Springのxmlの中につなげるフィルターを書き込むという方法が上記の設定になります(なるほどねええ)。このフィルターには(だいぶ以前とは違ってしまっていて、『浦島太郎』になってしまったあ)

  • Channel-Processing Filter(Optional)

    安全なチャンネルで伝送されているかどうかを確認する。今回は使わない。

  • ConcurrentSessionFilter

    「Concurrent Session Handling」をしたいときに必要だそうだけど、よくわからん。マニュアルによれば、「Batmanというユーザー名で、2つの異なるsessionからWeb Applicationへのロギングを停止させられる」などと書いてあるし、「ある一定以上、同じアプリケーションへの認証を同時にするということをAcegiは防ぐことが出来る」等とも書かれており、そのようなことをしたい場合は必要だそうです。でも今回は使わない。

  • HttpSessionContextIntegrationFilter

    Integration Filter(統一フィルター)は、HTTP Sessionに保存されているユーザーの認証情報を取り出そうと試みます。みつかったら、ContextHolderに保存されます。

  • Authentication processing mechanisms - AuthenticationProcessingFilter

    リクエストが認証リクエストかどうかを決定する。もしそうなら、usernameとpasswordをリクエストから取り出し、認証マネージャーにユーザーの身元を決定します。認証リクエストでないなら、次のフィルターチェーンに移動します。

  • SecurityContextHolderAwareRequestFilter

    serveletコンテナにHttpServletRequestWrapperを気づかせるために必要らしい。

  • AnonymousProcessingFilter

    最初の頃のプロセスメカニズムがSecurityContextHolderを更新しなかったら、anonymous Authentication objectが注入される。

    そもそも、このanonymous Authenticationとはデフォルトではアクセス不可だけれど、ある条件、例えばLogoutやLoginフォームなどの特殊なものだけはOKなどという場合、これを使って、実現するそうで、いつものようにURIに対してAntのパターンを使って実現できる。

  • ExceptionTranslationFilter

    Acegi Securityが出す例外を捕まえて、HTTPのエラーリスポンスを返すか、正しいAuthenticationEntryPointが実行されるようになる。

  • FilterSecurityInterceptor

    これでAntの表現で細かく、アクセスを制限できる。

が用意されています。なので、Springの設定ファイルの1つ、applicationContext.xmlは次にようになります。

    <bean id="filterChainProxy"
          class="org.acegisecurity.util.FilterChainProxy">
      <property name="filterInvocationDefinitionSource">
        <value>
          CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
          PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
        </value>
      </property>
    </bean>

	  

上記の3つのbeanの設定はそれぞれ次のようになります。出てくるのを追っていくと、きりがないので、「authenticationManager」のみ、出しています。

    <bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />

    <bean id="logoutFilter"
          class="org.acegisecurity.ui.logout.LogoutFilter">
      <constructor-arg value="/logoutSuccess.jsp" />
      <constructor-arg>
        <list>
          <bean
class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler" />
        </list>
      </constructor-arg>
    </bean>

    <bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
      <property name="authenticationManager"
                ref="authenticationManager" />
      <property name="authenticationFailureUrl"
                value="/user_loginerror.jsp" />
      <property name="defaultTargetUrl"
                value="/action/showContributionForm" />
      <property name="filterProcessesUrl"
                value="/action/j_acegi_security_check" />
    </bean>

    <bean id="securityContextHolderAwareRequestFilter"
class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter" \
    />


    <bean id="anonymousProcessingFilter"
class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
      <property name="key" value="changeThis" />
      <property name="userAttribute"
                value="anonymousUser,ROLE_ANONYMOUS" />
    </bean>


    <bean id="exceptionTranslationFilter"
          class="org.acegisecurity.ui.ExceptionTranslationFilter">
      <property name="authenticationEntryPoint">
        <bean
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
          <property name="loginFormUrl" value="/login.jsp" />
        </bean>
      </property>
      <property name="accessDeniedHandler">
        <bean
           class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
          <property name="errorPage" value="/accessDenied.jsp" />
        </bean>
      </property>
    </bean>


<bean id="authenticationManager" \
    class="org.acegisecurity.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref local="daoAuthenticationProvider"/>
            </list>
        </property>
    </bean>

	  

さて、次はAcegiのmanager等の設定等を書いていくことになりますが、その前に、まずはグランドプラン(というか、おおよそ何をするのかという大雑把な理解)を理解しましょう。

  • AuthenticationManager

    上記のフィルターのところで、フィルターから参照しているものとして1つだけ引用しました。これは、ユーザーの身元を調べるためのマネージャで、実装は別のproviderが使われる。

    • daoAuthenticationProvider

      これはユーザーの身元をDBから判断するというものです。

      • jdbcDaoImpl

        さらに「daoAuthenticationProvider」は「userDetailsService」というフィールドに対して、すでに実装されているjdbcDaoImplが必要になります。実際のSQL文などはここに書かれています。デフォルトの通りならこのまま使えますが、テーブル名が違うだとか、もう少し多くの情報を詰め込みたいなどの要求がル場合には、そのSQL文を設定xmlの中に書くとか、或いはこれをextendsしたものを作成する必要があるでしょう。

  • Cacheの設定
  • AccessDecisionManager

    これはAuthority(Role)でそのセキュアなリソースへの許可をどのように決めるか、を定義します。もともと3種類用意されていて。今回は「1つでもvoterが許可したらOK」というAffirmativeBasedというものを使います。

    • voter

      voterとは、もちろん、投票者ということですが、最初からはroleVoterが用意されており、これを使います(org.acegisecurity.vote.RoleVoter)。

さあ、これらを具体的に実装しますが、refで参照しているbeanの設定が深く、深く、深くネストしていたりして、世美豆良委場合もありますが、根気よく追って下さい。

<?xml version="1.0" encoding="Windows-31J"?>

<!DOCTYPE beans PUBLIC
    "-//SPRING//DTD BEAN//EN"
    "spring-beans.dtd">
<beans default-autowire="no" default-lazy-init="false" \
    default-dependency-check="none">
    <!-- データソース -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" \
    destroy-method="close">
        <property name="driverClassName">
            <value>org.postgresql.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:postgresql://localhost:5432/dum_bbs</value>
        </property>
        <property name="username">
            <value>chikkun</value>
        </property>
        <property name="password">
            <value>kazukun</value>
        </property>
    </bean>
    <!-- セッションファクトリ  -->
<bean id="sessionFactory" \
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation">
            <value>WEB-INF/classes/hibernate.cfg.xml</value>
        </property>
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
    </bean>

    <bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
        <property name="transactionAttributes">
            <props>
<prop key="save*">PROPAGATION_REQUIRED,-UserAlreadyExistException</prop>
            </props>
        </property>
    </bean>
    <!-- トランザクション設定 -->
    <bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>

    <!-- インターセプター for logging -->
<bean id="traceInterceptor" \
    class="org.springframework.aop.interceptor.TraceInterceptor"/>

    <bean id="userTransactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
        <property name="transactionAttributeSource">
            <value>
com.chikkun.common.login.database.UserDataUtils.save*=PROPAGATION_REQUIRED,-UserAlreadyExistException
com.chikkun.common.login.database.UserDataUtils.update*=PROPAGATION_REQUIRED
com.chikkun.common.login.database.UserDataUtils.delete*=PROPAGATION_REQUIRED
            </value>
        </property>
        <!--
        <property name="postInterceptors">
            <list>
                <value>throwsLoggingAdvisor</value>
            </list>
        </property>
        -->
    </bean>
    <bean id="userService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
        <property name="target">
            <ref bean="userDataUtilsTarget"/>
        </property>
        <property name="proxyInterfaces">
            <value>com.chikkun.common.login.database.IUserDataUtils</value>
        </property>
        <property name="transactionAttributes">
            <props>
                <prop key="save*">PROPAGATION_REQUIRED</prop>
                <prop key="update*">PROPAGATION_REQUIRED</prop>
                <prop key="delete*">PROPAGATION_REQUIRED</prop>
                <prop key="find*">PROPAGATION_REQUIRED, readOnly</prop>
            </props>
        </property>
    </bean>
<bean id="userDataUtilsTarget" \
    class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
            <ref bean="userDataUtils"/>
        </property>
        <property name="interceptorNames">
            <value>loggingInterceptor</value>
        </property>
    </bean>
<bean id="hibernateInterceptor" \
    class="org.springframework.orm.hibernate3.HibernateInterceptor">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
    <!--  Logging   -->
<bean id="throwsLoggingAdvice" \
    class="com.chikkun.jbbs.database.ThrowsLoggingAdvice"/>
<bean id="loggingInterceptor" \
    class="com.chikkun.common.LoggingInterceptor"/>

    <!--Acegi Security-->
    <bean id="filterChainProxy"
          class="org.acegisecurity.util.FilterChainProxy">
      <property name="filterInvocationDefinitionSource">
        <value>
          CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
          PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
        </value>
      </property>
    </bean>

    <bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />

    <bean id="logoutFilter"
          class="org.acegisecurity.ui.logout.LogoutFilter">
      <constructor-arg value="/logoutSuccess.jsp" />
      <constructor-arg>
        <list>
          <bean
class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler" />
        </list>
      </constructor-arg>
    </bean>

    <bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
      <property name="authenticationManager"
                ref="authenticationManager" />
      <property name="authenticationFailureUrl"
                value="/user_loginerror.jsp" />
      <property name="defaultTargetUrl"
                value="/action/showContributionForm" />
      <property name="filterProcessesUrl"
                value="/action/j_acegi_security_check" />
    </bean>

    <bean id="securityContextHolderAwareRequestFilter"
class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter" \
    />


    <bean id="anonymousProcessingFilter"
class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
      <property name="key" value="changeThis" />
      <property name="userAttribute"
                value="anonymousUser,ROLE_ANONYMOUS" />
    </bean>


    <bean id="exceptionTranslationFilter"
          class="org.acegisecurity.ui.ExceptionTranslationFilter">
      <property name="authenticationEntryPoint">
        <bean
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
          <property name="loginFormUrl" value="/login.jsp" />
        </bean>
      </property>
      <property name="accessDeniedHandler">
        <bean
           class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
          <property name="errorPage" value="/accessDenied.jsp" />
        </bean>
      </property>
    </bean>


<bean id="authenticationManager" \
    class="org.acegisecurity.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref local="daoAuthenticationProvider"/>
            </list>
        </property>
    </bean>


    <bean id="daoAuthenticationProvider"
        class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
        <property name="userDetailsService">
            <ref local="jdbcDaoImpl"/>
        </property>
        <property name="userCache">
            <ref local="userCache"/>
        </property>
        <property name="passwordEncoder">
            <ref local="passwordEncoder"/>
        </property>
    </bean>

<bean id="jdbcDaoImpl" \
    class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
    </bean>

<bean id="passwordEncoder" \
    class="org.acegisecurity.providers.encoding.PlaintextPasswordEncoder"/>

<bean id="userCache" \
    class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
        <property name="cache">
            <ref local="userCacheBackend"/>
        </property>
    </bean>
<bean id="userCacheBackend" \
    class="org.springframework.cache.ehcache.EhCacheFactoryBean">
        <property name="cacheManager">
            <ref local="cacheManager"/>
        </property>
        <property name="cacheName">
            <value>userCache</value>
        </property>
        <property name="timeToIdle"><value>120</value></property>
    </bean>

<bean id="cacheManager" \
    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>

    <bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
      <property name="authenticationManager" ref="authenticationManager" />
      <property name="accessDecisionManager" ref="accessDecisionManager" />
      <property name="objectDefinitionSource">
        <value>
          CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
          PATTERN_TYPE_APACHE_ANT
          /login.jsp=IS_AUTHENTICATED_ANONYMOUSLY
          /**=ROLE_USER,ROLE_ADMIN,ROLE_MANAGER
        </value>
      </property>
    </bean>

<bean id="accessDecisionManager" \
    class="org.acegisecurity.vote.AffirmativeBased">
      <property name="allowIfAllAbstainDecisions"
                value="false" />
      <property name="decisionVoters">
        <list>
          <bean class="org.acegisecurity.vote.RoleVoter" />
          <bean class="org.acegisecurity.vote.AuthenticatedVoter" />
        </list>
      </property>
    </bean>
</beans>