竹形誠司 ブログ
ご質問    »トピック一覧
掲示板へのスパムが多いため、「ご質問」のコーナーはユーザー登録制とさせていただきました。お手数ですが、上の「新規ユーザーの登録」メニューより登録をお願いします。
帳票Web
アプリケーション

受注開発始めました
詳しくは こちら
竹形 誠司 著/ラトルズ刊
JSP帳票アプリケーション実践開発入門
JSP帳票アプリケーション
実践開発入門

JSP業務アプリケーション短期開発入門
JSP業務アプリケーション
短期開発入門

Java+MySQL+Tomcatで始めるWebアプリケーション構築入門
Java+MySQL+Tomcatで始めるWebアプリケーション構築入門

Java+MySQL+Tomcatで作る掲示板とブログ
Java+MySQL+Tomcatで作る
掲示板とブログ
ココをいじるともっと快感
by KILROY[KILROY]
 『― Web アプリケーション構築入門』の P.365 には、「パラメータで渡される値に日本語が含まれる場合は、request オブジェクトの setCharacterEncoding メソッドで文字コードの種類を設定する必要がある」とあります。
 とはいえ、これをいちいち設定するのが面倒臭い、あるいは逆に「めったに使わないのでつい忘れ、文字化けを起こしてから考え込む」といった方もいらっしゃると思います(少なくとも私はそうです)。
 そこで、エンコーディングの設定を、Tomcat のフィルタとして設定することで、自動的に行なうという方法を紹介しておきます。ソースはこちら。
package spinstar;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class UnicodeFilter implements Filter {

    public void destroy() {}

    public void doFilter(
            ServletRequest request,
            ServletResponse response,
            FilterChain chain ) throws IOException, ServletException {
        if (request.getCharacterEncoding() == null) {
            request.setCharacterEncoding("UTF-8");
        }
        chain.doFilter(request, response);
    }

    public void init( FilterConfig filterConfig ) throws ServletException {
       
    }

}
 次に、これを web.xml にフィルタとして登録します。場所は web-app タグの中です。書式は以下のようなものです。
    <filter>
        <filter-name>UnicodeFilter</filter-name>
        <filter-class>spinstar.UnicodeFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>UnicodeFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
 以上で完了です。使用時にはアプリケーションのリロードをお忘れなく。

投稿:KILROY[KILROY]/2007年 03月 29日 10時 11分 /更新:2007年 03月 29日 10時 11分
ココをいじるともっと快感(その2)
by KILROY[KILROY]
 『Web アプリケーション構築入門』の P.195 には、「HTML では要素名と属性名は大文字と小文字が区別されないので、どちらで書いても構わないが、ソースコードの見やすさの点から本書では大文字で記述する。」とあります。
 ところが、XHTML となると、タグ名や属性名は小文字が原則です。
 で、ワタシは小文字に目が慣れちゃってるもんだから、コード追うのが正直ツラいです(T_T)。
 ついでながら、JaMyTo シリーズの Java のソースコードに埋め込まれている SQL 文も、大文字で書かれています。
 これ、Windows システム上の MySQL だとほぼ間違いなく動くんですが、一部の UNIX 系システム上の MySQL だと、まれに文法エラーになってしまいます。去年のことですが、確か MySQL バージョン 4.0 でこの問題点に引っかかった人がいました。
 ローカル環境(こちらは Windows )で実行を確認して、class ファイルを運用環境(こちらは UNIX)に放り込む、というスタイルで開発していたのでまだ助かったのですが(エラーメッセージでたいたい見当がつきます)、「CVS にチェックインして実行環境ではチェックアウトしたものを別途ビルドしてデプロイ」とかいったスタイルだと、けっこう障害の切り分けに梃子摺ります。「UNIX は小文字」というのもいまどき「常識」とは言いがたいので、お気をつけください。
投稿:KILROY[KILROY]/2007年 03月 29日 14時 33分 /更新:2007年 03月 29日 14時 33分
ココをいじるともっと快感(その3)
by KILROY[KILROY]
 『Web アプリケーション構築入門』のサンプルコードでは、JSTL や式言語を使っていません。まあ、それやっちゃうとますます本が厚くなってしまうので致し方ないのは存じておりますが、追っていてツラい部分はあります。
 そんな訳で、JSTL や式言語を活用するとこんな感じになりますよ、というサンプルをひとつ。お題は「登録ユーザだけが書き込める掲示板」のログイン部分である LoginProcess.jsp 。User クラスは多少修正して、ログインが成功したかどうかを保持する login エレメントを追加し、getter/setter も追加しました。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<%@ page contentType="text/html; charset=Windows-31J"%>
<%@ taglib prefix="core" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page isELIgnored="false" %>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=Windows-31J">
  <title>ログイン</title>
  <link rel="stylesheet" type="text/css" href="../style.css" />
</head>
<body>
  <jsp:useBean class="jamyto.bbs.User" id="user" scope="session"/>
  <h1>ログイン</h1>
  <div class="section">
<%
    String strUserName  = request.getParameter("user_name");
    String strPassword  = request.getParameter("password");
    String strAutoLogin = request.getParameter("auto_login");

    boolean blResult = user.load(strUserName, strPassword);

    String strMessage = null;
    try {
        if (blResult) {
            if (strAutoLogin != null) {
                Cookie coo1 = new Cookie("uid", Integer.toString(user.getId()));
                coo1.setMaxAge(60*60*24*31);
                coo1.setPath("/BBS");
                response.addCookie(coo1);
                Cookie coo2=new Cookie("auto_key", session.getId());
                coo2.setMaxAge(60*60*24*31);
                coo2.setPath("/BBS");
                response.addCookie(coo2);
                user.setAutoKey(session.getId());
            } else {
                Cookie coo1 = new Cookie("uid", "");
                coo1.setMaxAge(0);
                coo1.setPath("/BBS");
                response.addCookie(coo1);
                Cookie coo2 = new Cookie("auto_key", "");
                coo2.setMaxAge(0);
                coo2.setPath("/BBS");
                response.addCookie(coo2);
            }
            if (user.getUrl() != null) {
                response.sendRedirect(user.getUrl());
                return;
            }
        }
    } catch( Exception e ) {
        strMessage = e.getMessage();
    }
%>
    <core:if test="${!user.login}">
      ユーザ名またはパスワードが違います。<a href="javascript:history.back()">&nbsp;戻る&nbsp;</a>
    </core:if>
    <core:if test="${user.login}">
      ログインしました。
      <a href="<%=request.getContextPath()%>/bbs/User/PasswordUserEdit.jsp">パスワードの変更</a>
      <a href="<%=request.getContextPath()%>/bbs/topic/TopicList.jsp">掲示板へ</a>
      <core:if test="${user.priv == 3}">
        <a href="<%=request.getContextPath()%>/bbs/User/UserList.jsp">ユーザ管理</a>
      </core:if>
    </core:if>
  </div>
</body>
</html>
 実際にはもっと簡潔に書けるのではありますが、本日はここまでにしとう存じます。m(_ _)m
投稿:KILROY[KILROY]/2007年 03月 30日 13時 06分 /更新:2007年 03月 30日 13時 08分
ココをいじるともっと快感(その4)
by KILROY[KILROY]
 『Web アプリケーション構築入門』のサンプルアプリケーションである「登録ユーザだけが書き込める掲示板」では、ディレクトリ名も JSP のファイル名も、「大文字小文字混在スタイル」になっています。
 これ、UNIX 系システムだと何となく“やばそう”という気がしたので、移植性を高めるために名前を変更したら、いきなり Tomcat が混乱しました。

 ……さもありなん。(-_-;)

 そんな訳で、
eclipse の開発環境の上で別名(jamytox)のプロジェクトを立て、
JSP 以外のファイルを jamyto から jamytox へ移動し、
jamyto の bbs 以下の ディレクトリ/JSP ファイルをすべてエキスポートし、
ディレクトリ名とファイル名を変更(全部小文字で_で区切るスタイル)し、
一個一個動作を検証しながら中身の名前も実体に合わせる。
という作業をやってディレクトリ名とファイル名を変更しました。
 やれやれ、と思って jamyto プロジェクトを削除し、 jamytox を jamyto に名称変更したら、

 …… ディスクリプタ(アプリ名.xml)が壊れてて動かねぇ。_| ̄|○

 eclipse は server.xml を自動更新します。eclipse 上でファイル名の大改修を行なう際は、 conf/catalina/localhost 配下のファイルのバックアップをお忘れなく。

 なお、一般的に、ファイル名を大幅に変更する場合、 CVS (ソースコード管理システム)も混乱しちゃうので、ソースのバックアップは手で行なう以外に方法が(たぶん)ありません。けっこう危険な行為なので、ディレクトリ・ファイルの構成・名称の決定には、充分配慮することをお奨めします。
投稿:KILROY[KILROY]/2007年 03月 31日 12時 54分 /更新:2007年 03月 31日 12時 54分
ココをいじるともっと快感(その5)
by KILROY[KILROY]
 このところの四日間(途中いろいろあったので正味3人日)で、
『サーバアプリケーション開発入門』のオンラインショップ
『作る掲示板とブログ』の掲示板、ブログ、携帯ブログ
『Web アプリケーション構築入門』の掲示板
のTomcat 5.5 および MySQL 5.0 への移植、ディレクトリ構成・名称の整理とファイル名の整理、および画面遷移と基本動作の確認、終了いたしました。m(_ _)m
 あとは認証がらみのあれこれの検証やら画面周りの作りこみ(商用アプリケーションだとこの二点がいちばん重要なんですが(-_-;))その他作りこみを残すのみ(つーかこれが大変なんだ(-_-!))、という状態です。

 で、一段落ついたところで気になったのが、 DbConnection.java です。

 オンラインショップの例がいちばん分りやすいのでこいつで説明すると、
在庫や受発注の情報
アプリケーションにログインする際のアカウント情報
顧客の住所氏名などの個人情報
というのは、それぞれ別のデータベースに置くのが望ましいわけですが、こいつを  DbConnection で管理してしまうと、このあたりの切り分けがうまくいきません。
 そこで、
データベースとやりとりする部分をユーティリティクラスとして実装。
デフォルトの名前で特定のデータベースのコネクションを返す DbConnection
それぞれのデータベースのコネクションを返す、DbConnection のサブクラス
みたいな構成にすると使い勝手がいいんじゃないか、と思って試してみたんですが、まあそこそこに成功したようです。
 だけど、抽象クラスだのインタフェースだのいろいろ考えたんですが、きれいな実装方法が思いつきません。しばらく粘ってみるつもりなので、何か結果が出たらお知らせします。
 なお、現在自宅サーバー構築中(つーか回線待ち)なので、自宅鯖が立ったら公開予定です。では。
投稿:KILROY[KILROY]/2007年 04月 01日 22時 28分 /更新:2007年 04月 01日 22時 28分
ココをいじるともっと快感(その6)
by KILROY[KILROY]
> しばらく粘ってみるつもりなので、何か結果が出たらお知らせします。
 大した成果は上がっていないが、とりあえず結果が出たのでお知らせする。
 けっきょく、以下のような構成にしてみた。まず、データベース周りのユーティティクラス。
package spinstar;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

/**
* RDBMS のリソース管理用ユーティリティクラス。
* @author KILROY
*
*/
public class RelationalDataBaseManager {

    /**
    * データベースにアクセスする。
    * JNDI 使用バージョン(運用環境)
    * @return
    * @throws SQLException
    */
    public static Connection getConnection( String name ) throws SQLException {

        try {
            // データソースの取得
            DataSource ds = getDataSource(name);

            // データソースからConnectionを取得
            try {
                // Connection con = ds.getConnection("root", "KILROY");
                Connection con = ds.getConnection();
                if (con != null) {
                    System.out.println("カタログ名 : " + con.getCatalog());
                } else {
                    System.out.println("コネクションを取得できませんでした。");
                }

            //  Statementを取得
            //    Statement stmt = con.createStatement();
                return con;
            } catch ( SQLException sqle ){
                System.out.println("取得したデータソースからコネクションを取得できません。");
                sqle.printStackTrace();
            }
        } catch ( NamingException nmge ){
            System.out.println("JNDI のネーミング情報を解決できません。");
            nmge.printStackTrace();
        }
        return null;
    }

    /**
    * データソースからConnectionを取得
    * JNDI 使用バージョン(運用環境)
    * @return
    * @throws SQLException
    */
    public static Connection getConnection( DataSource ds ) throws SQLException {

        try {
            // Connection con = ds.getConnection("root", "KILROY");
            Connection con = ds.getConnection();
            if (con != null) {
                System.out.println("カタログ名 : " + con.getCatalog());
            } else {
                System.out.println("コネクションを取得できませんでした。");
            }

            return con;
        } catch ( SQLException sqle ){
            System.out.println("取得したデータソースからコネクションを取得できません。");
            sqle.printStackTrace();
        }
        return null;
    }

    /**
    *
    * @param name
    * @return
    * @throws NamingException
    */
    public static DataSource getDataSource( String name ) throws NamingException {
        DataSource ds = null;

        // 初期コンテキストを取得
        InitialContext initCtx = new InitialContext();

        final String jce = "java:comp/env";
        Context envCtx = (Context) initCtx.lookup(jce);

        // ルックアップしてデータソースを取得
        Object obj = envCtx.lookup(name);
        if (obj != null) {
            // System.out.println(obj.getClass().getCanonicalName());
        } else {
            System.out.println("JNDI のネーミングの解決に失敗しました。");
        }

        ds = (DataSource)obj;
        if (ds != null) {
            System.out.println(ds.toString());
            return ds;
        } else {
            System.out.println("データソースの取得に失敗しました。");
        }

        return ds;
    }

    /**
    * ローカルなデータベースにアクセスする。
    * 直接記述バージョン
    * @return
    * @throws SQLException
    */
    public static Connection getLocalConnection() throws SQLException {

        final String DRIVER = "com.mysql.jdbc.Driver";
        final String URL = "jdbc:mysql://localhost/bbs";

        final String USER = "root";
        final String PASS = "xyzzy";

        return getLocalConnection(DRIVER, URL, USER, PASS);
    }

    /**
    * データベースに直接アクセスする。
    * @return
    * @throws SQLException
    */
    private static Connection getLocalConnection(
            String driver,
            String url,
            String user,
            String password    ) throws SQLException {

        try {
                Class.forName(driver);
             
        } catch ( ClassNotFoundException e ) {
            e.printStackTrace();
            throw new IllegalStateException("fail to class load : " + e.getMessage());
        }
        Connection con = DriverManager.getConnection( url, user, password );
        return con;
    }
}
 上記クラスを用いて、 Connection を返すクラスを定義する。
package spinstar;

import javax.naming.NamingException;

import java.sql.Connection;
import javax.sql.DataSource;
import java.sql.SQLException;

public class RdbConnection {

    private static DataSource source = null;

    public static Connection getConnection()
            throws NamingException, SQLException {
        return getConnection("jdbc/mysql");
    }

    /**
    *
    * @param ResRefName
    * @return
    * @throws SQLException
    * @throws Exception
    */
    protected static Connection getConnection( String ResRefName )
            throws NamingException, SQLException {
        if (source == null) {
            source = RelationalDataBaseManager.getDataSource(ResRefName);
        }
        return  RelationalDataBaseManager.getConnection(source);
    }

}
 で、こいつをローカルに引きこんで、最終的にアプリケーション・ローカルなコネクションを渡すクラスを定義するのである。具体的には、こんな感じ。
package bbss;

import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.NamingException;

public class BbssDbConnection extends spinstar.RdbConnection {

    public static Connection getConnection()
            throws NamingException, SQLException  {
        return getConnection("jdbc/bbss");
    }
}
 「けっこういろいろ書いてあんじゃん」とツッコミが入りそうだが、これがワタシの能力の限界だ(「自分で限界を決めるんじゃないっ!」とか言わんように。工数とのやりくりっつーのもあるのだ)。てなワケで後進に期待する。
投稿:KILROY[KILROY]/2007年 04月 03日 22時 37分 /更新:2007年 04月 03日 22時 37分
ココをいじるともっと快感 ― 続報
by KILROY[KILROY]
 商用アプリケーションの開発のベースとしても十分に利用できる水準の、 JaMyTo シリーズのサンプルアプリケーションのソースを提供しようと改造を行なっていましたが、
『掲示板とブログ』の掲示板の index.jsp のスレッド表示部分のリファクタリングを進めているうちに開発用マシンのディスクの空き領域が足りなくなり、
JSP の展開中に作業領域不足で Tomcat の実行環境がクラッシュし、
Tomcat の再インストールを行なったところがうっかり旧環境の common/lib の下のライブラリを保存しておくのを忘れて MySQL を使ったアプリケーションが軒並み動かなくなり、
各種ライブラリをダウンロードしようと思ったら「ディスクの空き領域が足りません」とかいったメッセージが頻発し、
とはいえ消せるデータは消しつくしたし、バックアップが複数あって管理が面倒になってたこともあり、
しかたなく Buffaro の TeraStation を買ってきて余計なデータをそっちに移して空き領域を確保し、
やれやれと思ったら Win のセキュリティ・パッチの配布だの Java の JRE のバージョンアップだのが始まってしまい、

それも終わらせてようやく開発が再開しました。

 ……とはいえ、なんか知らないけど eclipse のコンソール・ウィンドウ見てたら Tomcat 5.5 インストールした筈なのに Tomcat 6 がどうこうとかいった妙なメッセージは出ているし、もはや自分が扱っている環境がどうなっているのかすらよく分らない状態です。

 このあたり、整理したらあらためてご報告したいと思います。では。
投稿:KILROY[KILROY]/2007年 04月 12日 09時 59分 /更新:2007年 04月 12日 10時 12分