2013年12月31日火曜日

JAVAの開発ディレクトリ構成ってどうするのがいいのだろう?

eclipse などを使って利用していると配布パッケージを自動的に作ってくれたりして、プロジェクトで見ているディレクトリ構成とは違ったものがサーバーの下にリリースされたりする訳だけれど、実際の開発では、一人=1プロジェクト=1パッケージというわけにも行かないだろうから、実際の開発現場ではどのように配布パッケージを作成しているのだろう。

(1) 普通の Hello World だとこんな風に紹介されることが多い気がします。
class test01 {
  public static void main(String args[]) {
    System.out.println("Hello World!");
  }
}
$ javac test01.java
$ java test01
Hello World!

(2) パッケージ名をつけて実行してみると classモジュールは test ディレクトリ配下にないと叱られます。
package test;
class test01 {
  public static void main(String args[]) {
    System.out.println("Hello World!");
  }
}
$ javac test01.java
$ java test01
Exception in thread "main" java.lang.NoClassDefFoundError: test01 (wrong name: test/test01)
 at java.lang.ClassLoader.defineClass1(Native Method)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:788)
 at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
 at java.net.URLClassLoader.defineClass(URLClassLoader.java:447)
 at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
 at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
 at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
 at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)
$ mkdir test
$ mv test01.class test
$ java test/test01
Hello World!
(3) javac -d オプションを指定するとパッケージディレクトリを作ってその下にクラスファイルを配置してくれます。
$ tree .
.
└── test01.java

0 directories, 1 file
$ javac -d . test01.java
$ tree .
.
├── test
│   └── test01.class
└── test01.java

1 directory, 2 files
$ java test/test01
Hello World!
(4) 配布パッケージにはソースコードは含まれないので、ソースフォルダとクラスフォルダは分けたいところです。やっぱり class ファイルの階層と src のファイル階層は同じにしたいところです。
$ tree .
.
├── classes
└── src
    └── test
        └── test01.java

3 directories, 1 file
$ javac -d classes src/test/test01.java
$ tree .
.
├── classes
│   └── test
│       └── test01.class
└── src
    └── test
        └── test01.java

4 directories, 2 files
$ cd classes
$ java test/test01
Hello World!

(5) WEB配布パッケージは基本構成が決まっているので、基本構成の中に class ファイルを配置するようにしてみます。
$ tree .
.
├── deploypkg.war
│   ├── META-INF
│   └── WEB-INF
│       ├── classes
│       └── lib
└── src
    └── test
        └── test01.java

7 directories, 1 file
$ javac -d deploypkg.war/WEB-INF/classes src/test/test01.java
$ tree .
.
├── deploypkg.war
│   ├── META-INF
│   └── WEB-INF
│       ├── classes
│       │   └── test
│       │       └── test01.class
│       └── lib
└── src
    └── test
        └── test01.java

8 directories, 2 files
$ cd deploypkg.war/WEB-INF/classes
$ java test/test01
Hello World!

(6) この形式でサーブレットプログラムをコンパイル、配布してみます。
$ tree .
.
├── deploypkg.war
│   ├── META-INF
│   └── WEB-INF
│       ├── classes
│       └── lib
└── src
    └── test
        └── test01.java

7 directories, 1 file

test01のソースコード
package test;

import java.io.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet(name="test01", urlPatterns={"/test01"})
public class test01 extends HttpServlet { 
  @Override
  public void doGet(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {

    InitialContext initctx = null;
    Connection con = null;

    try {
      initctx = new InitialContext();
      DataSource ds = (DataSource)initctx.lookup("java:jboss/datasources/MySqlDS");
      con = ds.getConnection();

      res.setContentType("text/html; charset=UTF-8");
      PrintWriter out = res.getWriter();

      Statement stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery("select * from foods;");

      out.println("<html>");
      out.println("<head>");
      out.println("<title> SERVLET-MYSQL-TEST </title>");
      out.println("</head>");
      out.println("<body>");
      out.println("<h3>果物一覧</h3>");
      out.println("<table>");
      out.println("<tr>");
      out.println("<th>CODE</th>");
      out.println("<th>名前</th>");
      out.println("<th>値段</th>");
      out.println("</tr>");

      while(rs.next()) {
        out.println("<tr>");
        out.println("<td>" + rs.getString("code")  + "</td>" );
        out.println("<td>" + rs.getString("name")  + "</td>" );
        out.println("<td>" + rs.getString("price") + "</td>" );
        out.println("</tr>");
      }

      out.println("</body>");
      out.println("</html>");

      con.close();
      initctx.close();
    }
    catch(NamingException e) {
      e.printStackTrace();
    }
    catch(SQLException e) {
      e.printStackTrace();
    }
  }
}

コンパイル・配備
$ javac -d deploypkg.war/WEB-INF/classes -classpath ~/jboss7/modules/javax/servlet/api/main/jboss-servlet-api_3.0_spec-1.0.0.Final.jar src/test/test01.java
$ tree .
.
├── deploypkg.war
│   ├── META-INF
│   └── WEB-INF
│       ├── classes
│       │   └── test
│       │       └── test01.class
│       └── lib
└── src
    └── test
        └── test01.java

8 directories, 2 files
$ cp -rp deploypkg.war ~/jboss7/standalone/deployments
$ touch ~/jboss7/standalone/deployments/deploypkg.war.dodeploy

実行

(7) deplypkg.war を jar で圧縮して配備してみます。touch ファイルを作らずともホットデプロイされました。
$ mv deploypkg.war deploypkg
$ cd deploypkg
$ jar cvf ../deploypkg.war .
マニフェストが追加されました
WEB-INF/を追加中です(入=0)(出=0)(0%格納されました)
WEB-INF/lib/を追加中です(入=0)(出=0)(0%格納されました)
WEB-INF/classes/を追加中です(入=0)(出=0)(0%格納されました)
WEB-INF/classes/test/を追加中です(入=0)(出=0)(0%格納されました)
WEB-INF/classes/test/test01.classを追加中です(入=2501)(出=1398)(44%収縮されました)
エントリMETA-INF/を無視します
$ cd ..
.
├── deploypkg
│   ├── META-INF
│   └── WEB-INF
│       ├── classes
│       │   └── test
│       │       └── test01.class
│       └── lib
├── deploypkg.war
└── src
    └── test
        └── test01.java

8 directories, 3 files
$ cp -rp deploypkg.war ~/jboss7/standalone/deployments


2013年12月30日月曜日

jboss7.1.1 と mysql の連携

最初は、standalone.xml を直に編集していたりしたが、Jboss7.1.1 は管理画面から登録するのが正道らしい。管理画面から、mysql ドライバーを配備して、jndi のソース登録して利用してみた。

Jboss7.1.1 で mysql 利用


(1) Manage Deployments より Add content を選択

(2) mysqljdbc ドライバファイルを選択

(3) 配備名の確認後保存

(4) Enable ボタンでドライバの有効化

(5) ここまで更新されたファイル
    <deployments>
        <deployment name="mysql-connector-java-5.1.27-bin.jar" runtime-name="mysql-connector-java-5.1.27-bin.jar">
            <content sha1="180296391137c12da3ba2a35dcc93ef23fb2c1ff"/>
        </deployment>
    </deployments>

そして standalone 配下の data ディレクトリが新たに作成、配備されている。
├── standalone
│   ├── data
│   │   ├── content
│   │   │   └── 18
│   │   │       └── 0296391137c12da3ba2a35dcc93ef23fb2c1ff
│   │   │           └── content
│   │   ├── timer-service-data
│   │   └── tx-object-store
│   │       └── ShadowNoFileLockStore
│   │           └── defaultStore
(6) Datasources の追加

(7) JNDI名などを設定

(8) JDBCドライバを選択

(9) 接続情報の設定

(10) Selection のPoolタブで接続プール数の設定

(11) ここまでの操作の server.xml への更新内容

(11) JSP で接続確認(構成)
$ !tree
tree test01.war
test01.war
└── test01.jsp

0 directories, 1 file

(12) JSP で接続確認(JSPソース)
<%@ page contentType="text/html; charset=UTF-8"
   pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<%@page
   import="java.sql.*,
            javax.sql.*,
            javax.naming.InitialContext,
            javax.naming.Context"%>
<title>JSPテスト</title>
</head>

<body>
<%
  InitialContext initctx = new InitialContext();
  DataSource ds = (DataSource)initctx.lookup("java:jboss/datasources/MySqlDS");
  Connection cn = ds.getConnection();

  Statement stmt = cn.createStatement();
  ResultSet rs = stmt.executeQuery("select * from foods;");
%>
<h1>果物一覧</h1>
<table>
   <tr>
      <th>code</th>
      <th>名前</th>
      <th>値段</th>
   </tr>
   <% while(rs.next()) { %>
   <tr>
      <td>
      <%= rs.getString("code") %>
      </td>
      <td>
      <%= rs.getString("name") %>
      </td>
      <td>
      <%= rs.getString("price") %>
      </td>
   </tr>
   <%
     }
     cn.close();
     initctx.close();
   %>
</table>
</body>
</html>

(13) JSP で接続確認(配備)
$ cp -rp test01.war ~/jboss7/standalone/deployments/
$ touch  ~/jboss7/standalone/deployments/test01.war.dodeploy

(13) JSP で接続確認(実行確認)


jboss7 を eclipse から使う。

私の場合は eclipse で開発するのは、自分が何をしているのか判らなくなるのであまり好きではない。しかし、一応、環境構築はしておこうと思って eclipse からサーバーの起動・停止をためしてみた。

Jboss tools の利用


(1) eclipse の server.view より new-server を選択
(2) Download additinal server adapter で JBossTools を選択してライセンス承諾

(3) 再起動後、server.view より再び new-server を選択

(4) 例によってDinamic Web Project でJSPを試す。


jboss7.1.1のインストール

JAVAの主要技術は、tomcat+servlet/JSP から j2ee を通り越して java EE に移り変わっているようです。その中でも、JSF、EJB、JPA と言ったキーワードに行き当たります。そんな訳で、アプリケーションサーバーの JBoss をインストールしてみました。近所の比較的大きな本屋や、インターネット検索でも、これらの情報は少なかったり古かったりするように感じます。大丈夫か自分!とおもいつつ利用してみたいと思います。

(1) パッケージの入手と展開
$ wget http://download.jboss.org/jbossas/7.1/jboss-as-7.1.1.Final/jboss-as-7.1.1.Final.zip
$ unzip jboss-as-7.1.1.Final.zip
$ mv jboss-as-7.1.1.Final ~/jboss7

(2) 管理ユーザの作成
$ cd ~/jboss7/bin
$ ./add-user.sh

What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): a

Enter the details of the new user to add.
Realm (ManagementRealm) : 
Username : taro
Password : 
Re-enter Password : 
About to add user 'taro' for realm 'ManagementRealm'
Is this correct yes/no? yes
Added user 'naoki' to file '/home/taro/jboss7/standalone/configuration/mgmt-users.properties'
Added user 'naoki' to file '/home/taro/jboss7/domain/configuration/mgmt-users.properties'

(3) 起動
./standalone.sh


TOMCAT:JSFの最小構成

Servlet, JSP と見ていくうちに JSFというものに行き当たった。とりあえず、これも動かす環境を作成してみた。

最初、web.xmlで<url-pattern>/faces/*</url-pattern>と書いた場合、http://localhost:8080/jsftest01/faces/test01.jsp と指定すると test01.jspが起動されるのに対し、web.xml で<url-pattern>*.jsf</url-pattern>と指定した場合、http://localhost8080/jsftest01/test01.jsf と指定すると test01.jsp が起動するということに気づくまで時間が掛かりました。最初に誰でも通る道なのでしょうか。

JSFを使ってみる


(1) ディレクトリ構成
jsftest01
├── WEB-INF
│   ├── lib
│   │   ├── javax.faces-2.2.1.jar
│   │   └── jstl-1.2.jar
│   └── web.xml
└── test01.jsp

2 directories, 4 files

(2) ライブラリの入手
wget http://repo1.maven.org/maven2/org/glassfish/javax.faces/2.2.4/javax.faces-2.2.4.jar
wget http://repo1.maven.org/maven2/javax/servlet/jstl/1.2/jstl-1.2.jar

(3) web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  id="WebApp_ID" version="2.5">
  
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>
</web-app>

(4) test01.jsp
<?xml version="1.0" encoding="utf-8" ?<
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html" version="1.2"<
  <jsp:directive.page language="java"
    contentType="text/html; charset=utf-8" pageEncoding="utf-8" /<
  <html xmlns="http://www.w3.org/1999/xhtml"<
    <head<
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /<
      <title<JSF Sample</title<
    </head<
    <body<
      <f:view<
        <h:outputText value="JSFテストページ" id="text1" /<
      </f:view<
    </body<
  </html<
</jsp:root<

(5) 配備
cp -rp jsftest01 ~/tomcat7/webapps

(6) 実行結果


2013年12月28日土曜日

TOMCAT:MYSQL:SERVLET:ストアドプロシージャ

ServletからストアドプロシージャでMYSQLのテーブルを参照、更新したみた。

Servletからストアドプロシージャを使う


(1) ディレクトリ構成
$ tree test06
test06
├── META-INF
│   └── context.xml
└── WEB-INF
    └── classes
        ├── test06.class
        └── test06.java

3 directories, 3 files
(2) テーブルとデータ
mysql -unaoki -ppassword sampledb <<EOF
drop table if exists foods;
create table foods(
  code  varchar(8) primary key,
  name  varchar(100),
  price integer
) engine=InnoDB;

insert into foods values('000001','りんご',398);
insert into foods values('000002','みかん',480);
insert into foods values('000003','柿'    ,450);

delimiter $$
drop procedure if exists ptest01$$
create procedure ptest01(
  in  p_code varchar(8),
  out p_name varchar(100),
  out p_price int,
  out p_count int
)
begin
  select name,price into p_name,p_price from foods where code = p_code;
  select row_count() into p_count;
end;
$$
drop procedure if exists ptest02$$
create procedure ptest02(
  in  p_code varchar(8),
  in p_name varchar(100),
  in p_price int,
  out p_count int
)
begin
  update foods
  set  name = p_name,
       price = p_price
  where code = p_code;
  select row_count() into p_count;
end;
$$
EOF
(3) コンテキストファイル

コネクションプールはDBCPとJDBCという2つの仕組みが提供されているらしい。TOCAT7ではデフォルトはDBCPらいしいが、切り替えるには、factory で指定できるらしい。jndi の定義は、本来は tomcat7/conf 配下の context.xml で設定するべきだと思うのだが、アプリケーション単位に設定できる。全く同じ jndi名で片方のアプリケーションがコネクションプールを使いきっても、もう一方のアプリケーションが独自に context.xml を使っていれば影響はなかった。サーバー管理としては、一箇所で管理したいところだとは思うが、個別にアプリをテストする時には便利な仕様である。
$ cat test02/META-INF/context.xml
<Context>
  <Resource name="jdbc/TestDB" 
            auth="Container" 
            type="javax.sql.DataSource"
            maxActive="100" 
            maxIdle="30" 
            maxWait="10000"
            username="naoki" 
            password="password" 
            driverClassName="com.mysql.jdbc.Driver"
            factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"
            url="jdbc:mysql://localhost:3306/sampledb"/>

<!--      factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" -->
</Context>
※ ~/tomcat7/lib 配下にmysql-connector-java-5.1.27-bin.jarを配置
(4) SERVLEAT(JAVA)ファイル
import java.io.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet(name="test06", urlPatterns={"/test06"})
public class test06 extends HttpServlet { 
  @Override
  public void doGet(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {

    res.setContentType("text/html; charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println(base_page());
    out.close();
  }

  private String base_page() {
    String HTML_TEXT = "<html>"
                     + "<head>"
                     + "<title> SERVLET-MYSQL-TEST </title>"
                     + "</head>"
                     + "<body>"
                     + "<h3>検索</h3>"
                     + "<form method='post' action='test06'>"
                     + "<input type='hidden' name='mode' value='select'>"
                     + "code:<input type='text' name='code'>"
                     + "<p>"
                     + "<input type='submit' value='検索'>"
                     + "</form>"
                     + "</body>"
                     + "</html>";
    return(HTML_TEXT);
  }

  @Override
  public void doPost(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {

    req.setCharacterEncoding("UTF-8");
    res.setContentType("text/html; charset=UTF-8");

    String mode = req.getParameter("mode");
    if (mode.equals("select")) {
      selectprocess(req,res);
    }
    else if (mode.equals("update")) {
      updateprocess(req,res);
    }
  }

  public void selectprocess(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {

    InitialContext initctx = null;
    Connection cn = null;
    int counter = 0;

    try {
      initctx = new InitialContext();
      DataSource ds = (DataSource)initctx.lookup("java:comp/env/jdbc/TestDB");
      cn = ds.getConnection();

      CallableStatement st = cn.prepareCall("{call ptest01(?,?,?,?)}");
      String code  = req.getParameter("code");

      st.setString(1,code);
      st.registerOutParameter(2,java.sql.Types.VARCHAR);
      st.registerOutParameter(3,java.sql.Types.INTEGER);
      st.registerOutParameter(4,java.sql.Types.INTEGER);

      st.execute();

      String name  = st.getString(2);
      Integer price = st.getInt(3);
      Integer count = st.getInt(4);

      if (count == 1) {
        PrintWriter out = res.getWriter();
        out.println(search_result_page(code,name,price));
        out.close();
      }
      else {
        PrintWriter out = res.getWriter();
        out.println(search_error_page(counter,code));
        out.close();
      }

      cn.close();
      initctx.close();
    }
    catch(NamingException e) {
      e.printStackTrace();
    }
    catch(SQLException e) {
      e.printStackTrace();
    }
    finally {
      try {
        if (cn != null) cn.close();
      }
      catch(SQLException e) {
        e.printStackTrace();
      }
      try {
        if (initctx != null) initctx.close();
      }
      catch(NamingException e) {
        e.printStackTrace();
      }
    }
  }

  private String search_result_page(String code, String name,Integer price) {
    String HTML_TEXT = "<html>"
                     + "<head>"
                     + "<title> SERVLET-MYSQL-TEST </title>"
                     + "</head>"
                     + "<body>"
                     + "<form method='post' action='test06'>"
                     + "<input type='hidden' name='mode' value='update'>"
                     + "code:%s" 
                     + "<input type='hidden' name='code' value='%s'>"
                     + "名前:<input type='text' name='name' value='%s'>"
                     + "値段:<input type='text' name='price' value='%d'>"
                     + "<p>"
                     + "<a href=\"test06\">戻る</a>"
                     + "<input type='submit' value='更新'>"
                     + "</form>"
                     + "</body>"
                     + "</html>";
    return(String.format(HTML_TEXT,code,code,name,price));
  }

  private String search_error_page(int counter,String code) {
    String HTML_TEXT = "<html>"
                     + "<head>"
                     + "<title> SERVLET-MYSQL-TEST </title>"
                     + "</head>"
                     + "<body>"
                     + "code : %s は検索件数 %d 件でした。<br />"
                     + "<a href=\"test06\">戻る</a>"
                     + "</body>"
                     + "</html>";
    return(String.format(HTML_TEXT,code,counter));
  }

  public void updateprocess(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {
    InitialContext initctx = null;
    Connection cn = null;

    try {
      initctx = new InitialContext();
      DataSource ds = (DataSource)initctx.lookup("java:comp/env/jdbc/TestDB");
      cn = ds.getConnection();

      CallableStatement st = cn.prepareCall("{call ptest02(?,?,?,?)}");
      String code  = req.getParameter("code");

      st.setString(1,req.getParameter("code"));
      st.setString(2,req.getParameter("name"));
      st.setInt(3,Integer.parseInt(req.getParameter("price")));
      st.registerOutParameter(4,java.sql.Types.INTEGER);

      st.execute();

      Integer counter = st.getInt(4);

      if (counter == 1) {
        PrintWriter out = res.getWriter();
        out.println(update_result_page(counter,req.getParameter("code")));
        out.close();
      }
      else {
        PrintWriter out = res.getWriter();
        out.println(search_error_page(counter,req.getParameter("code")));
        out.close();
      }

      cn.close();
      initctx.close();
    }
    catch(NamingException e) {
      e.printStackTrace();
    }
    catch(SQLException e) {
      e.printStackTrace();
    }
    finally {
      try {
        if (cn != null) cn.close();
      }
      catch(SQLException e) {
        e.printStackTrace();
      }
      try {
        if (initctx != null) initctx.close();
      }
      catch(NamingException e) {
        e.printStackTrace();
      }
    }
  }

  private String update_result_page(int counter,String code) {
    String HTML_TEXT = "<html>"
                     + "<head>"
                     + "<title> SERVLET-MYSQL-TEST </title>"
                     + "</head>"
                     + "<body>"
                     + "更新件数:%d 件"
                     + "<a href='test06'>検索画面へ</a>"
                     + "</body>"
                     + "</html>";

    return(String.format(HTML_TEXT,counter));
  }

  private String update_error_page(int counter,String code) {
    String HTML_TEXT = "<html>"
                     + "<head>"
                     + "<title> SERVLET-MYSQL-TEST </title>"
                     + "</head>"
                     + "<body>"
                     + "code : %s は更新件数 %d 件でした。<br />"
                     + "<a href=\"test06\">戻る</a>"
                     + "</body>"
                     + "</html>";
    return(String.format(HTML_TEXT,code,counter));
  }
}

(5)コンパイル
javac -cp ~/tomcat7/lib/servlet-api.jar test06/WEB-INF/classes/test06.java

(6)TOMCATへ配備
$ cp -rp test06 ~/tomcat7/webapps
(7)実行結果


2013年12月26日木曜日

TOMCAT:MYSQL:SERVLET:文字化け

TOMCAT+SERVLET+MYSQLのテーブル更新。何気なくデフォルトでインストールしたmysqlだと化けてしまったり、英語の Hello world のサンプルを利用していたりすると化けてしまったりと、最初は誰でも通る道なのでしょうか。
  • コーディングでの文字コード指定
    req.setCharacterEncoding("UTF-8");
    res.setContentType("text/html; charset=UTF-8");
    a
    
  • mysqlの文字コード指定(/etc/mysql/my.cnf)
    [client]
    default-character-set=utf8
    [mysqld]
    character-set-server=utf8
    [mysqldump]
    default-character-set=utf8
    [mysql]
    default-character-set=utf8
      

ServletのMYSQLテーブル更新


(1) ディレクトリ構成
$ tree test05
test05
├── META-INF
│   └── context.xml
└── WEB-INF
    └── classes
        ├── test05.class
        └── test05.java

3 directories, 3 files
(2) テーブルとデータ
$ mysql -unaoki -p sampledb
Enter password: 
mysql> create table foods(
    ->   code         varchar(8)   PRIMARY KEY,
    ->   name         varchar(100),
    ->   price        integer,
    ->   index(price)
    -> ) engine=InnoDB;
Query OK, 0 rows affected (0.12 sec)

mysql> insert into foods values('000001','りんご',398);
Query OK, 1 row affected (0.03 sec)

mysql> insert into foods values('000002','みかん',480);
Query OK, 1 row affected (0.04 sec)

mysql> insert into foods values('000003','柿'    ,450);
Query OK, 1 row affected (0.04 sec)

mysql> select * from foods;
+--------+-----------+-------+
| code   | name      | price |
+--------+-----------+-------+
| 000001 | りんご    |   398 |
| 000002 | みかん    |   480 |
| 000003 | 柿        |   450 |
+--------+-----------+-------+
3 rows in set (0.00 sec)
(3) コンテキストファイル

コネクションプールはDBCPとJDBCという2つの仕組みが提供されているらしい。TOCAT7ではデフォルトはDBCPらいしいが、切り替えるには、factory で指定できるらしい。jndi の定義は、本来は tomcat7/conf 配下の context.xml で設定するべきだと思うのだが、アプリケーション単位に設定できる。全く同じ jndi名で片方のアプリケーションがコネクションプールを使いきっても、もう一方のアプリケーションが独自に context.xml を使っていれば影響はなかった。サーバー管理としては、一箇所で管理したいところだとは思うが、個別にアプリをテストする時には便利な仕様である。
$ cat test02/META-INF/context.xml
<Context>
  <Resource name="jdbc/TestDB" 
            auth="Container" 
            type="javax.sql.DataSource"
            maxActive="100" 
            maxIdle="30" 
            maxWait="10000"
            username="naoki" 
            password="password" 
            driverClassName="com.mysql.jdbc.Driver"
            factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"
            url="jdbc:mysql://localhost:3306/sampledb"/>

<!--      factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" -->
</Context>
(4) SERVLEAT(JAVA)ファイル
import java.io.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet(name="test05", urlPatterns={"/test05"})
public class test05 extends HttpServlet { 
  @Override
  public void doGet(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {

    res.setContentType("text/html; charset=UTF-8");
    PrintWriter out = res.getWriter();
    out.println(base_page());
    out.close();
  }

  private String base_page() {
    String HTML_TEXT = "<html>"
                     + "<head>"
                     + "<title> SERVLET-MYSQL-TEST </title>"
                     + "</head>"
                     + "<body>"
                     + "<h3>検索</h3>"
                     + "<form method='post' action='test05'>"
                     + "<input type='hidden' name='mode' value='select'>"
                     + "code:<input type='text' name='code'>"
                     + "<p>"
                     + "<input type='submit' value='検索'>"
                     + "</form>"
                     + "</body>"
                     + "</html>";
    return(HTML_TEXT);
  }

  @Override
  public void doPost(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {

    req.setCharacterEncoding("UTF-8");
    res.setContentType("text/html; charset=UTF-8");

    String mode = req.getParameter("mode");
    if (mode.equals("select")) {
      selectprocess(req,res);
    }
    else if (mode.equals("update")) {
      updateprocess(req,res);
    }
  }

  public void selectprocess(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {

    InitialContext initctx = null;
    Connection cn = null;
    int counter = 0;

    try {
      initctx = new InitialContext();
      DataSource ds = (DataSource)initctx.lookup("java:comp/env/jdbc/TestDB");
      cn = ds.getConnection();

      PreparedStatement st = cn.prepareStatement(
                             "select * from foods where code = ?");
      st.setString(1,req.getParameter("code"));
      ResultSet rs = st.executeQuery();
      String code = "";
      String name = "";
      String price = "";
      while(rs.next()) {
        code = rs.getString("code");
        name = rs.getString("name");
        price = rs.getString("price");
        counter++;
      }

      if (counter == 1) {
        PrintWriter out = res.getWriter();
        out.println(search_result_page(code,name,price));
        out.close();
      }
      else {
        PrintWriter out = res.getWriter();
        out.println(search_error_page(counter,code));
        out.close();
      }

      cn.close();
      initctx.close();
    }
    catch(NamingException e) {
      e.printStackTrace();
    }
    catch(SQLException e) {
      e.printStackTrace();
    }
    finally {
      try {
        if (cn != null) cn.close();
      }
      catch(SQLException e) {
        e.printStackTrace();
      }
      try {
        if (initctx != null) initctx.close();
      }
      catch(NamingException e) {
        e.printStackTrace();
      }
    }
  }

  private String search_result_page(String code, String name,String price) {
    String HTML_TEXT = "<html>"
                     + "<head>"
                     + "<title> SERVLET-MYSQL-TEST </title>"
                     + "</head>"
                     + "<body>"
                     + "<form method='post' action='test05'>"
                     + "<input type='hidden' name='mode' value='update'>"
                     + "code:%s" 
                     + "<input type='hidden' name='code' value='%s'>"
                     + "名前:<input type='text' name='name' value='%s'>"
                     + "値段:<input type='text' name='price' value='%s'>"
                     + "<p>"
                     + "<a href=\"test05\">戻る</a>"
                     + "<input type='submit' value='更新'>"
                     + "</form>"
                     + "</body>"
                     + "</html>";
    return(String.format(HTML_TEXT,code,code,name,price));
  }

  private String search_error_page(int counter,String code) {
    String HTML_TEXT = "<html>"
                     + "<head>"
                     + "<title> SERVLET-MYSQL-TEST </title>"
                     + "</head>"
                     + "<body>"
                     + "code : %s は %d 件でした。<br />"
                     + "<a href=\"test05\">戻る</a>"
                     + "</body>"
                     + "</html>";
    return(String.format(HTML_TEXT,code,counter));
  }

  public void updateprocess(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {
    InitialContext initctx = null;
    Connection cn = null;

    try {
      initctx = new InitialContext();
      DataSource ds = (DataSource)initctx.lookup("java:comp/env/jdbc/TestDB");
      cn = ds.getConnection();

      PreparedStatement st = cn.prepareStatement(
                             "update foods "
                           + "set name  = ?,"
                           + "    price = ? "
                           + "where code = ?");

      st.setString(1,req.getParameter("name"));
      st.setInt(2,Integer.valueOf(req.getParameter("price")));
      st.setString(3,req.getParameter("code"));
      int counter = st.executeUpdate();

      PrintWriter out = res.getWriter();
      out.println(update_result_page(counter,req.getParameter("code")));
      out.close();

      cn.close();
      initctx.close();
    }
    catch(NamingException e) {
      e.printStackTrace();
    }
    catch(SQLException e) {
      e.printStackTrace();
    }
    finally {
      try {
        if (cn != null) cn.close();
      }
      catch(SQLException e) {
        e.printStackTrace();
      }
      try {
        if (initctx != null) initctx.close();
      }
      catch(NamingException e) {
        e.printStackTrace();
      }
    }
  }

  private String update_result_page(int counter,String code) {
    String HTML_TEXT = "<html>"
                     + "<head>"
                     + "<title> SERVLET-MYSQL-TEST </title>"
                     + "</head>"
                     + "<body>"
                     + "更新件数:%d 件"
                     + "<a href='test05'>検索画面へ</a>"
                     + "</body>"
                     + "</html>";

    return(String.format(HTML_TEXT,counter));
  }
}

(5)コンパイル
javac -cp ~/tomcat7/lib/servlet-api.jar test05/WEB-INF/classes/test05.java

(5)TOMCATへ配備
$ cp -rp test5 ~/tomcat7/webapps
(6)実行結果
(7)mysqlで確認
mysql> select * from foods;
+--------+-------------------+-------+
| code   | name              | price |
+--------+-------------------+-------+
| 000001 | りんご(袋詰)      |   398 |
| 000002 | みかん            |   480 |
| 000003 | 柿                |   450 |
+--------+-------------------+-------+
3 rows in set (0.00 sec)

mysql> 


TOMCAT:SERVLET最小構成(MYSQL接続)

eclipse 上で JSPやServlet のHello worldを作成しても、実際にはどのような構成でサーバー上に配備されるか解りづらい。SevletからMYSQLを利用する最小構成を確認してみた。

ServletのMYSQL接続


(1) ディレクトリ構成
$ tree test04
test04
├── META-INF
│   └── context.xml
└── WEB-INF
    └── classes
        ├── test04.class
        └── test04.java

3 directories, 3 files
s
(2) テーブルとデータ
$ mysql -unaoki -p sampledb
Enter password: 
mysql> create table foods(
    ->   code         varchar(8)   PRIMARY KEY,
    ->   name         varchar(100),
    ->   price        integer,
    ->   index(price)
    -> ) engine=InnoDB;
Query OK, 0 rows affected (0.12 sec)

mysql> insert into foods values('000001','りんご',398);
Query OK, 1 row affected (0.03 sec)

mysql> insert into foods values('000002','みかん',480);
Query OK, 1 row affected (0.04 sec)

mysql> insert into foods values('000003','柿'    ,450);
Query OK, 1 row affected (0.04 sec)

mysql> select * from foods;
+--------+-----------+-------+
| code   | name      | price |
+--------+-----------+-------+
| 000001 | りんご    |   398 |
| 000002 | みかん    |   480 |
| 000003 | 柿        |   450 |
+--------+-----------+-------+
3 rows in set (0.00 sec)
(3) コンテキストファイル

コネクションプールはDBCPとJDBCという2つの仕組みが提供されているらしい。TOCAT7ではデフォルトはDBCPらいしいが、切り替えるには、factory で指定できるらしい。jndi の定義は、本来は tomcat7/conf 配下の context.xml で設定するべきだと思うのだが、アプリケーション単位に設定できる。全く同じ jndi名で片方のアプリケーションがコネクションプールを使いきっても、もう一方のアプリケーションが独自に context.xml を使っていれば影響はなかった。サーバー管理としては、一箇所で管理したいところだとは思うが、個別にアプリをテストする時には便利な仕様である。
$ cat test02/META-INF/context.xml
<Context>
  <Resource name="jdbc/TestDB" 
            auth="Container" 
            type="javax.sql.DataSource"
            maxActive="100" 
            maxIdle="30" 
            maxWait="10000"
            username="naoki" 
            password="password" 
            driverClassName="com.mysql.jdbc.Driver"
            factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"
            url="jdbc:mysql://localhost:3306/sampledb"/>

<!--      factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" -->
</Context>
(4) SERVLEAT(JAVA)ファイル
$ cat test04/WEB-INF/classes/test04.javaimport java.io.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet(name="test04", urlPatterns={"/test04"})
public class test04 extends HttpServlet { 
  @Override
  public void doGet(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {

    InitialContext initctx = null;
    Connection con = null;

    try {
      initctx = new InitialContext();
      DataSource ds = (DataSource)initctx.lookup("java:comp/env/jdbc/TestDB");
      con = ds.getConnection();

      res.setContentType("text/html; charset=UTF-8");
      PrintWriter out = res.getWriter();

      Statement stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery("select * from foods;");

      out.println("<html>");
      out.println("<head>");
      out.println("<title> SERVLET-TEST </title>");
      out.println("</head>");
      out.println("<body>");
      out.println("<h3>果物一覧</h3>");
      out.println("<table>");
      out.println("<tr>");
      out.println("<th>CODE</th>");
      out.println("<th>名前</th>");
      out.println("<th>値段</th>");
      out.println("</tr>");

      while(rs.next()) {
        out.println("<tr>");
        out.println("<td>" + rs.getString("code")  + "</td>" );
        out.println("<td>" + rs.getString("name")  + "</td>" );
        out.println("<td>" + rs.getString("price") + "</td>" );
        out.println("</tr>");
      }

      out.println("</body>");
      out.println("</html>");

      con.close();
      initctx.close();
    }
    catch(NamingException e) {
      e.printStackTrace();
    }
    catch(SQLException e) {
      e.printStackTrace();
    }
  }
}

(5)コンパイル
javac -cp ~/tomcat7/lib/servlet-api.jar test04/WEB-INF/classes/test04.java

(5)TOMCATへ配備
$ cp -rp test4 ~/tomcat7/webapps
(4)実行結果


TOMCAT:JSP最小構成でMYSQL接続

eclipse 上で JSPやServlet のHello worldを作成しても、実際にはどのような構成でサーバー上に配備されるか解りづらい。JSPからMYSQLを利用する最小構成を確認してみた。

JSPのMYSQL接続


(1) ディレクトリ構成
$ tree test02
test02
├── META-INF
│   └── context.xml
└── test02.jsp

1 directory, 2 files
(2) テーブルとデータ
$ mysql -unaoki -p sampledb
Enter password: 
mysql> create table foods(
    ->   code         varchar(8)   PRIMARY KEY,
    ->   name         varchar(100),
    ->   price        integer,
    ->   index(price)
    -> ) engine=InnoDB;
Query OK, 0 rows affected (0.12 sec)

mysql> insert into foods values('000001','りんご',398);
Query OK, 1 row affected (0.03 sec)

mysql> insert into foods values('000002','みかん',480);
Query OK, 1 row affected (0.04 sec)

mysql> insert into foods values('000003','柿'    ,450);
Query OK, 1 row affected (0.04 sec)

mysql> select * from foods;
+--------+-----------+-------+
| code   | name      | price |
+--------+-----------+-------+
| 000001 | りんご    |   398 |
| 000002 | みかん    |   480 |
| 000003 | 柿        |   450 |
+--------+-----------+-------+
3 rows in set (0.00 sec)
(3) コンテキストファイル

コネクションプールはDBCPとJDBCという2つの仕組みが提供されているらしい。TOCAT7ではデフォルトはDBCPらいしいが、切り替えるには、factory で指定できるらしい。jndi の定義は、本来は tomcat7/conf 配下の context.xml で設定するべきだと思うのだが、アプリケーション単位に設定できる。全く同じ jndi名で片方のアプリケーションがコネクションプールを使いきっても、もう一方のアプリケーションが独自に context.xml を使っていれば影響はなかった。サーバー管理としては、一箇所で管理したいところだとは思うが、個別にアプリをテストする時には便利な仕様である。
$ cat test02/META-INF/context.xml
<Context>
  <Resource name="jdbc/TestDB" 
            auth="Container" 
            type="javax.sql.DataSource"
            maxActive="100" 
            maxIdle="30" 
            maxWait="10000"
            username="naoki" 
            password="password" 
            driverClassName="com.mysql.jdbc.Driver"
            factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"
            url="jdbc:mysql://localhost:3306/sampledb"/>

<!--      factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" -->
</Context>
(4) JSPファイル
$ cat test02/test02.jsp
<%@ page contentType="text/html; charset=UTF-8"
   pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<%@page
   import="java.sql.*,
            javax.sql.*,
            javax.naming.InitialContext,
            javax.naming.Context"%>
<title>JSPテスト</title>
</head>

<body>
<%
  InitialContext initctx = new InitialContext();
  DataSource ds = (DataSource)initctx.lookup("java:comp/env/jdbc/TestDB");
  Connection cn = ds.getConnection();

  Statement stmt = cn.createStatement();
  ResugtSet rs = stmt.executeQuery("select * from foods;");
%>
<h1>果物一覧</h1>
<table>
   <tr>
      <th>code</th>
      <th>名前</th>
      <th>値段</th>
   </tr>
   <% while(rs.next()) { %>
   <tr>
      <td>
      <%= rs.getString("code") %>
      </td>
      <td>
      <%= rs.getString("name") %>
      </td>
      <td>
      <%= rs.getString("price") %>
      </td>
   </tr>
   <%
     }
     cn.close();
     initctx.close();
   %>
</table>
</body>
</html>
(5)TOMCATへ配備
$ cp -rp test02 ~/tomcat7/webapps
(4)実行結果


TOMCAT:SERVLET最小構成

eclipse 上で JSPやServlet のHello worldを作成しても、実際にはどのような構成でサーバー上に配備されるか解りづらい。SERVLETの最小構成を確認してみた。

SERVLETの最小構成


(1) ディレクトリ構成
$ tree test03
test03
└── WEB-INF
    └── classes
        ├── test03.class
        └── test03.java

2 directories, 2 files
(2) SEVLEAT(JAVA)ファイル
$ cat test03/WEB-INF/classes/test03.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet(name="test03", urlPatterns={"/test03"})
public class test03 extends HttpServlet { 
  @Override
  public void doGet(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException,IOException {

    res.setContentType("text/html; charset=UTF-8");
    PrintWriter out = res.getWriter();

    out.println("");
    out.println("");
    out.println("こんにちわ");
    out.println("");
    out.println("");

    out.close();

  }
}
(3) コンパイル
$ javac -cp ~/tomcat7/lib/servlet-api.jar test03/WEB-INF/classes/test03.java
(4)TOMCATへ配備
$ cp -rp test03 ~/tomcat7/webapps
(5)実行結果

2013年12月25日水曜日

TOMCAT:JSP最小構成

eclipse 上で JSPやServlet のHello worldを作成しても、実際にはどのような構成でサーバー上に配備されるか解りづらい。まずはJSPの最小構成を確認してみた。

JSPの最小構成


(1) ディレクトリ構成
$ tree test01
test01
└── test01.jsp

0 directories, 1 file
(2) JSPファイル
$ cat test01/test01.jsp
<%@ page contentType="text/html; charset=UTF-8"
   pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSPテスト</title>
</head>
<body>
<%out.println("JSP:テスト1");%>
</body>
</html>
(3)TOMCATへ配備
$ cp -rp test01 ~/tomcat7/webapps
(4)実行結果


2013年12月23日月曜日

ubuntu13.10+tomcat7+eclipse(juno)環境構築

ubuntu 上で eclipse を使って tomcat 上の servlet や jsp を作成している人というのは、どれくらいいるのでしょうか。意外と少ないのでしょうね。

恥ずかしながら、 tomcat は、apache のプラグインなのか、単独の webserver なのかもよく知りませんでした。どうやら、tomcat は単体で webserver として利用できますが、静的ページの処理性能や信頼性の理由から、apache 経由で利用することが多いらしいです。

ubuntu  では、sudo apt-get install tomcat7 とすれば、簡単に使えれるようになりますが、eclipse から tomcat の起動をしたり、モジュールのデプロイする場合には、ubuntu のデフォルトのインストールは使いづらいです。今回は本家から直接ダウンロードして、$HOMEの下にインストールしました。

●tomcat7のインストール
% wget http://ftp.jaist.ac.jp/pub/apache/tomcat/tomcat-7/v7.0.47/bin/apache-tomcat-7.0.47.tar.gz
% tar xvfz apache-tomcat-7.0.47.tar.gz
% mv apache-tomcat-7.0.47 ~/tomcat7
% cd ~/tomcat7/bin
% ./startup.sh


次は、eclipse をインストールする。ubuntu の sudo apt-get install eclipse でインストールできるが、これもバージョン3.8だったりするのと、インストール先がマルチユーザー向けの設定なので、これも本家から直接ダウンロードして$HOMEの下にインストールした。(バージョン4.3は ubuntu13.10 だとメニューがうまく表示されないので、 4.2(juno)を選択した)

●eclipseのインストール
% wget http://download.springsource.com/release/ECLIPSE/juno/SR2/eclipse-jee-juno-SR2-linux-gtk-x86_64.tar.gz
% tar xvfz eclipse-jee-juno-SR2-linux-gtk-x86_64.tar.gz
% mv eclipse ~/
% cd ~/eclipse
% ./eclipse

●eclipseとtomcatの関連付け

●eclipse より tomcat を起動


次に eclipse で、servlet と jsp を作成し、eclipse から実行します。(eclipse のプロジェクトフォルダは色々なフォルダが沢山作成されて良くわからないのが悲しい。)
●最初に eclipse でプロジェクトを作成(file->Dynamic Web Project)

パッケージ作成(New->Package)

●サーブレット作成(New->Servlet)

 /**
  * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
  */
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  // TODO Auto-generated method stub
     response.setContentType("text/html");
     PrintWriter out = response.getWriter();
     out.println("Hello world!");
 }

●作成したサーブレットの実行

●JSPの作成(New->JSP)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




Insert title here


<% out.println("Hello world!") %>



●JSPの実行(run as->run on server)

●tomcat サーバの下に作成されるもの
eclipse の開発フォルダから、サーバーに適したファイル構成にして配置してくれるようです。eclipse は自動で色々やってくれるのが便利なのでしょうが、何やってるのか良く把握した上で利用しないと原因不明のトラブルに当たりそうです。。
naoki@naoki-iMac:~/tomcat7$ ls -al
合計 132
drwxr-xr-x 11 naoki naoki  4096 12月 22 23:59 .
drwxr-xr-x 52 naoki naoki  4096 12月 23 13:00 ..
-rw-r--r--  1 naoki naoki 56812 10月 18 19:21 LICENSE
-rw-r--r--  1 naoki naoki  1192 10月 18 19:21 NOTICE
-rw-r--r--  1 naoki naoki  8826 10月 18 19:21 RELEASE-NOTES
-rw-r--r--  1 naoki naoki 16262 10月 18 19:21 RUNNING.txt
drwxr-xr-x  2 naoki naoki  4096 12月 22 15:53 backup
drwxr-xr-x  2 naoki naoki  4096 12月 22 14:07 bin
drwxr-xr-x  3 naoki naoki  4096 12月 22 14:15 conf
drwxr-xr-x  2 naoki naoki  4096 12月 22 14:07 lib
drwxr-xr-x  2 naoki naoki  4096 12月 23 00:00 logs
drwxr-xr-x  2 naoki naoki  4096 12月 23 12:52 temp
drwxr-xr-x  8 naoki naoki  4096 12月 22 22:20 webapps
drwxr-xr-x  3 naoki naoki  4096 12月 22 14:09 work
drwxr-xr-x  4 naoki naoki  4096 12月 23 12:40 wtpwebapps
naoki@naoki-iMac:~/tomcat7$ find wtpwebapps -print
wtpwebapps
wtpwebapps/ROOT
wtpwebapps/ROOT/WEB-INF
wtpwebapps/ROOT/WEB-INF/web.xml
wtpwebapps/test01
wtpwebapps/test01/WEB-INF
wtpwebapps/test01/WEB-INF/lib
wtpwebapps/test01/WEB-INF/classes
wtpwebapps/test01/WEB-INF/classes/jp
wtpwebapps/test01/WEB-INF/classes/jp/naomusi
wtpwebapps/test01/WEB-INF/classes/jp/naomusi/helloworld.class
wtpwebapps/test01/helloworld.jsp
wtpwebapps/test01/META-INF
wtpwebapps/test01/META-INF/MANIFEST.MF