argius note

プログラミング関連

Eclipse4.4JavaEE + Tomcat7 でJavaEE6開発環境を構築

Eclipse JavaEE 4.4 (Eclipse IDE for Java EE Developers Luna) とTomcat7を使って、JavaEE6の開発環境を1から構築します。
JavaEE6入門ではなく、JavaEE6入門のためのEclipse環境を作るのが目的です。


今回はスクリーンショット多めにしてみました。


(同日 22:00頃)記述ミスと記述不足を20箇所ほど修正しました。概要の文章を少し手直ししました。


目次

  • 概要
  • 前提条件
  • 初期設定
  • ソフトウェアのインストール
  • Eclipseの起動
  • プロジェクトの作成
  • プロジェクトの設定変更(Mavenの設定変更)
  • デバッグ用サーバーでHello World !
  • EL式の動作確認
  • JSTLの動作確認
  • テスト用データベースの準備 (H2database)
  • JSTLのデータベース接続 (1) 直接接続
  • JSTLのデータベース接続 (2) データソース
  • Servletの動作確認
  • ビルド・デプロイ設定
  • (おわりに)

概要

私が実際に使っているJavaEEはかなりバージョンが古く、その知識はほとんどJavaEE 5.0よりも前で止まっています。
そのせいで最近ちょっと困ったことがあったので、今後のためにも、基礎知識として比較的新しいJavaEEを学んでおく必要があるかな、そしてどうせなら、一部のJavaEE機能を使うだけではなく、純粋なJavaEEでどこまで開発できるのかを知っておいた方が良いかな、と思ったのが今回の記事を書いたきっかけでした。


そうするには、少なくとも、実際に試すことのできる環境を構築しておかなければと思い、色々と試行錯誤の結果、EclipseJavaEE開発するにはやはりfor JavaEE Developerを使うのが簡単、という結論に落ち着きました。最初はMavenだけでやってみようとも考えていましたが、ちょっと無理でした。


それと、この記事は、どうしてもEclipseを使いたい人向けです。
試していませんが、NetBeansIntelliJのほうが簡単に構築・開発できるかも知れません。


以下の内容を含んでいます。

前提条件

JavaEclipseの基本操作は知っている前提です。
初心者向けではありません。手順書に近いものです。


実際に動作を確認したのは下記の環境ですが、Windowsに特化した内容ではありません。
記事の中では、パスの区切り表記は、主にスラッシュ(/)を使います。Windows固有のものはバックスラッシュも使います。

初期設定

ディレクトリー*1の構成を決めておきます。

Windows7での例を記載しておきます。これ以降の説明では、ディレクトリーを示すのにこれらの名前を使用します。

  • HOME (%USERPROFILE%, Unix系では$HOME)
    • jee6 (%USERPROFILE%\jee6=C:\Users\argius\jee6)
      • $TOMCAT_HOME (.\jee6\apache-tomcat-7.0.56)
      • database (.\jee6\database)
      • $ECLIPSE_HOME (.\jee6\eclipse-jee-luna-SR1*2 )
      • workspace (.\jee6\workspace)

JDKだけは、インストールウィザードに従ってインストールすることになります。
サンプルでは、H2databaseは相対パスの方が都合が良いので、HOMEディレクトリーの下に作っていますが、必ずしもこの通りでなくても構いません。

Eclipseを使うだけならば環境変数の設定は不要ですが、コマンドプロンプト(ターミナル)からTomcatMavenを実行する場合は、環境変数$JAVA_HOMEの設定が必要です。


Windowsの場合は、文字エンコーディング設定が初期設定でMS932になっています。ここは、UTF-8にしておくのが無難です。今回のサンプルもUTF-8で書くようにしています。


ソフトウェアのインストール

  • JDKのインストール

Java SE - Downloads | Oracle Technology Network | Oracleから、Java7以上のJDKをダウンロードしてインストールします。既にインストール済みであれば無視してください。

Eclipse IDE for Java EE Developers | Packagesの"Download Links"の下にあるリンクから、OSに対応したパッケージをダウンロードし、$ECLIPSE_HOMEに展開します。

Apache Tomcat - Apache Tomcat 7 Downloadsから、Binary Distributionsの下のCoreのいずれか(ZIPで良いでしょう)をダウンロードし、$TOMCAT_HOMEに展開します。

Eclipseの起動

$ECLIPSE_HOMEディレクトリーにあるeclipse(.exe)を実行します。
初回起動時に、ワークスペースの場所を尋ねられます。デフォルトでは$HOME/workspaceになります。今回は$HOME/jee6/workspaceにします。
また、Use this as the default and don ont ask again チェックボックスをオンにすれば、次回の起動時にはここで選択したワークスペースが自動的に選択されます。後で変えることもできます。

f:id:argius:20141108001056p:plain
Eclipse初回起動時にワークスペースの場所を尋ねられる

Installed JREにJDK7が設定されていることを確認しましょう(メニューの[workspace][Preference]の設定ダイアログを開き、[Java][Installed JREs]を選択)。
JREに設定されている場合は、JDKの設定を追加して、JDKをデフォルト(チェックボックスをオン)にしておきます。


プロジェクトの作成

メニューの[File][New][Maven Project]を選択して、ウィザードを起動します。
最初のページはそのままにして[Next]ボタンを押します。
2ページ目では、ひな形となるArchetypeを選択します。ここでは、maven-archetype-webappを選択し、[Next]ボタンを押します。

f:id:argius:20141108003413p:plain
2ページ目: ひな形のArchetypeを選択

3ページ目では、プロジェクトのArchetypeを入力します。私はnet.argius.webapp1としています。

f:id:argius:20141108003530p:plain
3ページ目: プロジェクトのArchetypeを入力して完了


おそらく数秒で、Mavenのプロジェクト初期化が完了します。


プロジェクトの設定変更(Mavenの設定変更)

このあと、部分ごとにMavenの設定(pom.xml)を修正していきます。完成版はGistにアップしておきます。


この時点では、4箇所を変更する必要があります。

  • コンパイラーがJava1.5になっている
  • JSPエラーが出ている
  • web.xmlがバージョン2.3になっている
  • Dynamic Web Moduleがバージョン2.3になっている

また、この時点でディレクトリーの無いソースフォルダーが2つ(src/main/java,src/test/java)ありますので、必要に応じて作成します。ここでは無くてもOKです。



最初に、ビルド設定を行います。maven-compiler-pluginを設定します。

pom.xmlを開くと、POMエディターで開きます。下側にあるタブのpom.xmlを選択すると、直接入力できるようになります。
buildの部分を以下のように書き換えます。入力後、保存するのを忘れないようにしましょう。

  • maven-compiler-pluginの設定
  <build>
    <finalName>webapp1</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
    </plugins>
  </build>

今回は、Java1.7がターゲットなのでこうなります。エンコーディングは、プロジェクトの設定に合わせます。



この時点では、JSPエラー(JSP Problems)が表示されています。これは、servlet-apiのライブラリーが設定されていないからです。*3

POMエディターのDependencyを選択すると、依存関係を設定するページが開きます。
既にjunitが設定済みです。
エディターを使って入力できます。こちらも入力後に保存するのを忘れないようにしましょう。

  • dependency: javax.servlet-apiの設定
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>


これを設定すると、今度はMavenエラー(Maven Problems)が表示されます。「Updateを実行しろ」というメッセージが出ていますので、アップデートを実行します。[Quick Fix]から(エラーメッセージを右クリック→[Quick Fix])でも実行できます。



次に、web.xmlsrc/main/webapp/WEB-INF/web.xml)の変更です。
maven-archetype-webappでプロジェクトを作成すると、web.xmlがバージョン2.3で作成されます。
今回は、Servlet 3.0を使用しますので、web.xmlがバージョン3.0に変更します。

下記ページに見本がありますので、参考にしてください。他のバージョンのものも記載されています。


最後に、Dynamic Web Moduleのバージョンを設定します。
プロジェクトのプロパティーを開いて、[Project Facets]を選択します。Dynamic Web Moduleは初期状態では2.3になっています。
さて、ここが重要なのですが、このままバージョンを3.0にするとなぜかOKが押せません。ここは一旦、チェックを外してOKします。再度開くと、3.0になっていて、チェックすると今度はOKができるようになっていますので、OKします。

f:id:argius:20141108155149p:plain
一度チェックを外してから再度ダイアログを開くと、3.0になっているので、チェックし直す


良く見ないと分かりませんが、プロジェクトの下にある"Deployment Descriptor"のアイコンが3.0になります。

f:id:argius:20141108224703p:plain
Deployment Descriptorのアイコンが3.0になっている


ここでもう一度、Mavenアップデート(プロジェクトを右クリック → [Maven][Update Project])を実行しておきます。


これで、Servlet 3.0を利用する環境が整いました。


デバッグ用サーバーでHello World !

それでは、最初からあるindex.jspを表示してみます。

プロジェクトを右クリックして、[Debug As][Debug On Server] を実行すると、ダイアログが表示されます。
この時点ではサーバーが未設定ですので、"Apache Tomcat 7.0 Server"を選択し、次のページ([Next])へ。

f:id:argius:20141108234054p:plain
デバッグサーバーの選択画面。server typeの窓をスクロールして、Apacheの下にある"Tomcat 7.0 Server"を選択する


次に、Tomcatのインストールディレクトリー(installation directory)を聞かれますので、$TOMCAT_HOMEを設定(変数は使えませんので、実際のパスを設定)し、次のページ([Next])へ。
なお、このページは次回も同じサーバーを使う場合はスキップされます。

f:id:argius:20141108234643p:plain
Tomcat 7.0 Serverの設定。ここでTomcatをインストールすることもできるがバージョンが古い


最後に次に、起動するサーバーにクラスパスを通すプロジェクトを設定します。右側のCongifuredにweabpp1があればOKです。[Finish]を押して完了します。



Eclipse上でWebブラウザーが起動し、"Hello World!"が表示されるはずです。
サーバーが上手く起動しない場合は、Mavenのアップデートを実行してから再度サーバーを起動してみてください。


EL式の動作確認

簡単なEL式を書いてみます。ページスコープに値を設定し、それをEL式で出力させます。

<html>
<body>
<% pageContext.setAttribute("msg", "Hello World!"); %>
<h2>${ pageScope.msg.toUpperCase() }</h2>
</body>
</html>

更新ボタンを押して、表示が変わることを確認しましょう。


ちなみに、web.xml2.3を使うと、デフォルトではEL式が無効(isELdisabled=true)になるので注意です。
詳しくは、下記ページを参照。



JSTLの動作確認

EL式は、JSTLと組み合わせるともっと使いやすくなります。JSTLとEL式があれば、ディレクティブやコメント以外では従来のJSPタグ(<% %>)を使わなくてもJSPを書くことができます。

JSPの先頭行に、taglibディレクティブを宣言します。

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

追加すると、JSPエラーが出ます。これは、JSTLライブラリーが未設定のためです。
servlet-apiと同様に、POMエディターのDependencyで追加しましょう。

  • dependency: jstlの設定
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
      <scope>runtime</scope>
    </dependency>

JSTLは実行時にも必要なので、scopeはruntimeに設定します。


body内の適当な行に、

<p><c:set var="msg2" value="Goodbye!" /> msg2=${ msg2 } </p>

を書きます。
サーバーを再起動して、更新ボタンを押して、表示が変わることを確認しましょう。

なお、c:setは書く場所によってはJSP警告が出ることがあります。ブロックタグの中に書けば出なくなったりしますが、発生条件が良く分かりません。


テスト用データベースの準備 (H2database)

今回は、テスト用データベースにH2databaseバージョン1.4を使用します。データベースファイルは、$HOME/jee6/database/webapp1.mv.dbになるようにします。この場合、JDBC-URLはjdbc:h2:file:~/jee6/database/webapp1になります。さらに、AutoServerモードを有効(jdbc:h2:file:~/jee6/database/webapp1;AUTO_SERVER=TRUE)にしておけば、複数のクライアントから同時接続することも可能になります。


H2databaseを使用するには、まずJDBCドライバーを取得します。
POMの依存関係にH2databaseを追加します。

  • dependency: h2databaseの設定
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.4.182</version>
      <scope>runtime</scope>
    </dependency>

$HOME/.m2/repository/com/h2database/h2/1.4.182/h2-1.4.182.jarがダウンロードされているのを確認します。


次に、データベースに接続します。
H2databaseは、スタンドアローンでJARを実行することで、Webベースのデータベースクライアントが起動します。
なお、Eclipse JEEでは、Data Source Explorerビューを使って、データベースに接続できます。これを使ってH2databaseに接続するには、Generic JDBC で設定します。

f:id:argius:20141109180119p:plain
H2databaseはブラウザーベースのクライアントが利用できる

接続したら、DDLとINSERTを実行します。何でも良いです。

create table table1 (
  id varchar(16) primary key,
  name varchar(64)
);

INSERT INTO table1 VALUES('001', 'argius');
INSERT INTO table1 VALUES('002', 'zzz');

JSTLのデータベース接続 (1) 直接接続

JSTLSQLアクションを使って、データベースにアクセスしてみましょう。


SQLアクションを使用するためのtaglibディレクティブを追加します。
それから、sql:setDataSourceでデータソースを設定します。

  • index.jsp SQLアクションの例
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql"%>
<html>
<body>
<sql:setDataSource
  var="ds"
  driver="org.h2.Driver"
  url="jdbc:h2:file:~/jee6/database/webapp1;AUTO_SERVER=TRUE"
  user="sa"
  password="sa" />
<sql:query var="rs" dataSource="${ds}">select * from table1</sql:query>
<table>
<c:forEach var="r" items="${rs.rows}">
  <tr><td>${ r.name }</td></tr>
</c:forEach>
</table>
</body>
</html>

ここでもJSPに警告が出ますが、正常に実行されると消えます。消えなくても無視してください。


JSTLのデータベース接続 (2) データソース

毎回、JSPでデータソースを設定するのは無駄が多いので、名前参照できるように設定しましょう。

JDBCドライバーは、Webコンテナー(Tomcat7)側に持たせておきます。$HOME/.m2/repository/com/h2database/h2/1.4.182/h2-1.4.182.jarを、$TOMCAT_HOME/libにコピーします。
前項でH2databaseの依存関係を追加している場合は、削除して、アプリケーションに直接JDBCドライバーを持たせないようにします。


Eclipse上ではServersの下にある、デバッグ時に接続しているTomcat7の設定に、データソースを追加します。

Project ExplorerのServers/ v7 .../の下に、context.xmlがありますので、そこにデータソース設定を追加します。
このアプリケーションを実際にデプロイする場合は、デプロイ先環境と同じ名前のResourcenameを合わせておくと良いでしょう。

  • context.xmlResourceを追加
<Resource
  name="jdbc/mydb"
  auth="Container"
  type="javax.sql.DataSource"
  username="sa"
  password="sa"
  driverClassName="org.h2.Driver"
  url="jdbc:h2:file:~/jee6/database/webapp1;AUTO_SERVER=TRUE"
  validationQuery="select 1"
  maxActive="10"
  maxIdle="2" />

JSP側では、データソースを名前参照するように修正します。

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql"%>
<html>
<body>
<sql:setDataSource var="ds" dataSource="jdbc/mydb" />
<sql:query var="rs" dataSource="${ds}">select * from table1</sql:query>
<table>
<c:forEach var="r" items="${rs.rows}">
  <tr><td>${ r.name }</td></tr>
</c:forEach>
</table>
</body>
</html>

Tomcatの設定を変更したので、サーバーを再起動させてから動作確認してください。


Servletの動作確認

サーブレットを動かしてページを表示できるようにして見ましょう。

ウィザードで[New][Servlet]で追加すると、web.xmlが壊れてしまうので、ウィザードで作られた雛形を元に、ウィザードを使わずにサーブレットを作ってみました。これを、普通の「新規クラスの追加」([New][Class])で追加します。

src/main/javaが作られていない場合は、ここで追加します。

  • webapp1.SearchServletクラス (src/main/java/webapp1/SearchServlet.java)
package webapp1;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

/**
 * Servlet implementation class SearchServlet
 */
@WebServlet("/search")
public final class SearchServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public SearchServlet() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        List<String> a = getNames();
        request.setAttribute("rows", a);
        RequestDispatcher dispatcher = request.getRequestDispatcher("/search.jsp");
        dispatcher.forward(request, response);
    }

    private List<String> getNames() throws ServletException {
        List<String> a = new ArrayList<String>();
        try {
            InitialContext ctx = new InitialContext();
            DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/mydb");
            try (Connection conn = ds.getConnection();
                 Statement stmt = conn.createStatement();
                 ResultSet rs = stmt.executeQuery("select name from table1")) {
                while (rs.next()) {
                    a.add(rs.getString(1));
                }
            }
        } catch (SQLException |NamingException e) {
            throw new ServletException(e);
        }
        return a;
    }

}


Servlet 3.0のアノテーションをちょっとだけ使っています。
@WebServletアノテーションを使うと、従来はweb.xmlに記述していたサーブレットマッピングが不要になります。

データベースへのアクセスは、今回はEJBJPAは扱いませんので、NamingAPIとJDBCを使っています。


出力先ページは、search.jspとします。

  • search.jsp (src/main/webapp/search.jsp)
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Search Result</title>
</head>
<body>
<h3>Search Result</h3>
<table>
<c:forEach var="r" items="${ rows }">
  <tr><td>${ r }</td></tr>
</c:forEach>
</table>
</body>
</html>

サーバーを再起動してからlocalhost:8080/webapp1/searchにアクセスし、ページが表示できればOKです。


ビルド・デプロイ設定

まずは、WARファイルをビルドする設定を追加します。

pom.xmlbuild-pluginsの下に、maven-war-pluginを追加します。

  • build-plugin: maven-war-pluginの設定
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.5</version>
      </plugin>

Run AsでMaven BuildでGoalsに war:war を入力し、実行すれば、target/webapp1.warが作られます。
autodeployに対応しているWebコンテナーならば、これをそのままautodeployディレクトリーに置けばデプロイされます。



最後に、テスト環境とは別のTomcat7サーバーに、WARをデプロイしてみましょう。

まず、デプロイの準備を行います。

  • MavenのTomcat7プラグインの設定
  • Tomcatに設定追加(ユーザー設定、manager-scriptモードを有効に)
  • Mavensettings.xmlに設定追加(デプロイ先Tomcatへのログイン設定)

その後、Eclipseからでなく、直接Tomcat7を起動します。($JAVA_HOMEの設定をお忘れなく)


設定内容は、下記を参考にしました。

一部だけ書いておきます。

  • build-plugin: tomcat7-maven-pluginの設定
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <server>mydeployserver</server>
          <port>8080</port>
          <path>/webapp1</path>
          <update>true</update>
        </configuration>
      </plugin>

settings.xmlには、mydeployserverの設定を追加します。


Goals = tomcat7:deploy を実行すれば、デプロイされます。
update=trueを設定しないと、上書き配備できませんので注意してください。

メッセージに

OK - コンテキストパス /webapp1 でアプリケーションを配備しました

が出力されていれば、デプロイは成功です。


ここまで設定してきたpom.xmlは、もちろん直接Mavenで使うことができます。説明は割愛しますが、Maven3をスタンドアローンでインストールしてコマンドラインからの実行を試してみてください。


デプロイは、実際の開発では、Jenkinsなどで動作させることになるでしょう。
この場合、Jenkinsを動作させる環境のsettings.xmlにも設定を追加する必要があるので忘れないように。


おわりに

ここまで作るのには、一部のハマりポイントを除けば、情報もそれなりにありますし、この内容が無くてもそれほど苦労しないと思います。
なので、最初は一部分だけの説明にとどめようと思いましたが簡単には行かず、結局は全部入りになってしまいました。


ご参考になれば幸いです。

*1:Windowsでは「フォルダー」が一般的ですが、Windowsに特化した内容ではないので、ディレクトリーに統一しています。

*2:Eclipseはzipを展開すると eclipse ディレクトリーがルートになります。複数のバージョンを管理するのでなければ、eclipse のままで構いません。

*3:index.jspやweb.xmlを用意しているのだから、servlet-apiも付けておいてくれれば良いのに。