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


0 件のコメント:

コメントを投稿