Visual Studio Code + Docker + Remote DevelopmentでTomcat上のWebアプリを超簡単にデバッグ

こんにちは、Visual Studio Codeを愛してやまないサイオステクノロジー技術部 武井(Twitter:@noriyukitakei)です。今回は、Tomcat上のWebアプリケーションをVisual Studio Codeを使ってデバッグしたいと思います。しかも従来の方法とは違い、ローカルPCの環境を全く汚さない方法でトライします。

Tomcat上で動くWebアプリのデバッグ

Tomcat上で動作するWebアプリケーションのIDEといえばEclipseですよね。でもVisual Studio Codeでも、それ出来ます!!

まずは「従来のやり方」をご紹介します。その後で、ローカルPCの環境を汚さない「すごいやり方」をご紹介します。

従来のやり方

Visual Studio Codeには「Tomcat for Java」という便利なプラグインがあり、これを使うことで、Tomcat上で動作するWebアプリケーションのデバッグを行うことができます。

では早速やってみましょう。まずJavaの拡張機能プラグインである「Java Extension Pack」を入れます。これを入れると、メソッドの参照元とかを一瞬で抽出出来たりと、開発に必要な補助機能がたくさん入ります。

画面左メニュー部の、下から2つ目の四角が4つ並んでいるアイコンをクリックして、「java extension pack」と入力しますと、「Java Extension Pack」が表示されますので、それをクリックします。そして緑色の「Install」ボタンをクリックします。これでプラグインのインストールは完了です。

 

次に「Tomcat for Java」のプラグインを入れます。先程と同様に、下から2つ目の四角が4つ並んでいるアイコンをクリックして、「tomcat for java」と入力しますと、「Tomcat for Java」が表示されますので、それをクリックします。そして緑色の「Install」ボタンをクリックします。これでプラグインのインストールは完了です。

 

次にWebアプリケーションの雛形をMavenで作成します。任意のディレクトリで以下のコマンドを実施して下さい。

$ mvn archetype:generate -DgroupId=com.example \
    -DartifactId=helloworld \
    -DarchetypeArtifactId=maven-archetype-webapp \
    -DinteractiveMode=false

 

下記のようなファイルとディレクトリが出来上がります。

helloworld
├── pom.xml
└── src
    └── main
        ├── resources
        └── webapp
            ├── WEB-INF
            │   └── web.xml
            └── index.jsp

 

サンプルのサーブレットを作成します。以下のようなファイルを作成します。

package com.example;

import java.io.*;
import javax.servlet.*;
import javax.servlet.https.*;

public class Hello extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
    PrintWriter out;

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

    out.println("<html><body>");
    out.println("<h1>Hello World!</h1>");
    out.println("</body></html>");
  }
}

 

先程作成したHello.javaを以下のようなディレクトリを作成して配置します。

helloworld
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── Hello.java ← ここに配置
        ├── resources
        └── webapp
            ├── WEB-INF
            │   └── web.xml
            └── index.jsp

 

pom.xmlを以下のように修正します。ソースとターゲットのバージョンを指定します。また、Servertを作成するためにServletのAPIをコンパイル時のみ使えるようにします。

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>helloworld</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>helloworld Maven Webapp</name>
  <url>https://maven.apache.org</url>
  <!-- ここから追加 -->
  <properties>
    <maven.compiler.source>1.6</maven.compiler.source>
    <maven.compiler.target>1.6</maven.compiler.target>
  </properties>
  <!-- ここまで追加 -->
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <!-- ここから追加 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>compile</scope>
    </dependency>
    <!-- ここまで追加 -->
  </dependencies>
  <build>
    <finalName>helloworld</finalName>
  </build>
</project>

 

web.xmlに以下の内容を追加します。おなじみのServletのURLマッピングですね。

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "https://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!-- ここから追加 -->
  <servlet>
    <servlet-name>Hello</servlet-name>
    <servlet-class>com.example.Hello</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
  <!-- ここまで追加 -->
</web-app>

Tomcatをダウンロードして解凍します(2020年1月22日時点での8.5系最新バージョンです)。

$ wget https://ftp.riken.jp/net/apache/tomcat/tomcat-8/v8.5.50/bin/apache-tomcat-8.5.50.tar.gz
$ tar xzvf apache-tomcat-8.5.50.tar.gz

 

解凍して出来たTomcatディレクトリをMavenプロジェクト直下に移動します。以下のようになります。

helloworld
├── apache-tomcat-8.5.50
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── Hello.java
        ├── resources
        └── webapp
            ├── WEB-INF
            │   └── web.xml
            └── index.jsp

 

Visual Studio Code左メニュー部最上部の四角が2つ重なっているアイコンをクリックして「Open Folder」をクリックします。そして先程作成した「helloworld」のディレクトリを選択します。

 

コマンドパレットを起動します。

 

「Tomcat: Add Tomcat Server」を選択します。

 

先程Tomcatを解凍したディレクトリ(apache-tomcat-8.5.50)を選択します。

 

画面左下部に「TOMCAT SERVERS」というのが表示され、その中に先程選択したTomcatが表示されます。右クリックして「Start」をクリックします。

 

下記のように緑色になれば、正常に起動した証拠です。

 

「helloworld」ディレクトリに移動し、以下のコマンドを実行してwarファイルを作成します。

$ mvn package

 

targetディレクトリ配下に「helloworld.war」ができますので、それを右クリックして「Debug on Tomcat Server」をクリックします。

 

Hello.javaを開いて、任意の地点にブレークポイントを打ちます。

 

「https://127.0.0.1:8080/helloworld/hello」にブラウザでアクセスします。すると先程ブレークポイントを打ったところで止まります!!ステキーΣ(゚∀゚ノ)ノキャー

すごいやり方

もっとすごい方法をご紹介します。先程の方法をおさらいするために図にしてみました。

上図からわかりますように、ローカルPCにTomcat、Java、Maven、Tomcat for Javaプラグインと様々なものが必要でした。ローカルのPCにこんなにたくさん入れたくないですよね。しかもJavaとかTomcatのバージョンを変えたい場合に大変なことになります(T_T)たくさんのバージョンのJavaを入れなければいけませんし、切り替えも大変です。

そこで、Visual Studio CodeのRemote Development機能を使います。具体的には、Docker上にTomcat環境を構築し、Remote Development機能を使ってDockerに接続してDocker上のTomcatを使ってデバッグします。さらにこの構成ですと、Tomcat for JavaプラグインもDocker上で動作しますので、本当にローカルPCには何も入れなくていいのです。図にすると以下のような構成になります。

では、早速構築してみましょう。

先程からポチポチ登場しているRemote Development機能について詳細をご説明します。これ、すごい機能なんです。Visual Studio Codeの中でも私の一番のお気に入りの機能です!!

Visual Studio Codeには、Remote Development という拡張機能があり、これをインストールすると、リモートサーバー上のコードを直接編集が可能です。SSHで接続可能なリモートサーバーやコンテナ、WSL上にあるファイルをVisual Studio Codeから直接編集できるのです。つまり、ローカルマシンにソースコードを持つ必要がありません。リモートサーバー上のファイルを、あたかもローカルマシンにあるファイルのように扱うことができます。

また、Remote Developmentを使うと、Visual Studio Codeの拡張機能をリモートサーバーにインストールし、リモートサーバー上で動作させることができます。

例えば、Visual Studio CodeのPHPの拡張機能であるPHP IntelliSenseはPHP7以上が必要になります。この場合、従来の方法では、ローカルマシンにPHP7をインストールする必要がありました。しかし、せっかくソースコードは、リモートサーバーに隔離出来たのに、プラグインを動作させるために、ローカルマシンにPHPインストールするなんて、ちょっとナンセンスだと思いませんか?

そこで、Visual Studio CodeのRemote Developmentを使うと拡張機能をリモートサーバー上にインストールできます。つまり、先程のPHP IntelliSenseはリモートサーバー上で動作するので、リモートサーバー上にPHP7が動作していればよいのです。

このように、Remote Developmentを使うと、ソースコードや拡張機能の実行環境を完全にリモートサーバー側に分離が可能であり、ローカルマシン側にはVisual Studio Code以外、何もインストールする必要がありません。ローカルマシンの環境を一切汚さず、開発環境を構築できるのはVisual Studio Codeの強みです。

ではRemote Developmenをインストールします。Remote Development自体もVisual Studio Codeの拡張機能です。

左部メニュー最下部にあるアイコン(四角が4つ並んでいるもの)をクリックし、「remote development」と入力し、Remote Developmentの拡張機能をインストールします。

Picture12

 

次に新しいディレクトリを作成し、以下のようにファイルを作成します。

helloworld
├── .devcontainer
│   ├── Dockerfile
│   └── devcontainer.json
├── .vscode
│   └── settings.json
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── Hello.java
        ├── resources
        └── webapp
            ├── WEB-INF
            │   └── web.xml
            └── index.jsp

srcディレクトリとpom.xmlは先程作成したものからまんまコピーしたものです。そして今回新しく作成する必要があるのは、以下の2つです。

  • .devcontainerディレクトリとその配下のファイル
  • .vscodeディレクトリとその配下のファイル

まず.devcontainer/Dockerfileの内容から説明します。コンテナを作成するために必要なおなじみのものです。

# Tomcatのイメージをダウンロードします。
FROM tomcat:8.5.49-jdk8-openjdk

# ローカルのソースコードをマウントするためのディレクトリを作成します。
RUN mkdir /opt/project

# Mavenをインストールします。
RUN apt-get update && \
    apt-get -y install maven

 

次に.devcontainer/devcontainer.jsonの内容です。VS Codeからコンテナを生成するための設定を記載します。

{
  # 任意の名前でOKです。
  "name": "Hello",

  # Remoteでログインしたいコンテナを作成するためのDockerfileの名称を指定します。
  "dockerFile": "Dockerfile",

  # この設定によりホストOSの8080宛の通信がコンテナの8080に転送されます。
  "forwardPorts": [8080],

  # コンテナに入ったときに最初にここで指定したものがカレントディレクトリになります。
  "workspaceFolder": "/opt/project",

  # ホストOSのVisual Studio Codeのプロジェクト直下のディレクトリをコンテナの/opt/projectディレクトリにマウント>します。
  "mounts": [
    "source=${localWorkspaceFolder},target=/opt/project,type=bind,consistency=cached"
  ]

  # コンテナが生成されたときに、コンテナ側にインストールする拡張機能です。
  # これを指定しないと、コンテナが再生成すると拡張機能が消えます。
  # 今回はJava Extension PackとTomcat for Javaをインストールします。
  "extensions": [
    "adashen.vscode-tomcat",
    "vscjava.vscode-java-pack"
  ]
}

 

次に.vscode/settings.jsonの内容です。これはHot Code Replaceの定義です。これをautoにしておくと、javaファイルを変更したときに、わざわざwarを作り直さなくても、自動的にTomcatに変更内容が反映されます。

{
    "java.debug.settings.hotCodeReplace": "auto"
}

 

これで準備は整いました。Remote Developmentの機能でコンテナに接続します。Visual Studio Codeの左隅にある緑のボタンをクリックします。

 

すると以下のようなリストが表示されますので、「Remote-Containers: Open Folder in Container…」 をクリックします。そして先程作成したディレクトリを選択します。

 

初回はちょっと時間がかかります。

 

コマンドパレットを起動します。

 

「Tomcat: Add Tomcat Server」を選択します。

 

コンテナ側のTomcatディレクトリである「/usr/local/tomcat」を選択して、「OK」をクリックします。

 

画面左下部に「TOMCAT SERVERS」というのが表示され、その中に先程選択したTomcatが表示されます。右クリックして「Start」をクリックします。

 

下記のように緑色になれば、正常に起動した証拠です。

 

TERMINALにて「mvn package」と入力してwarファイルを作成します。

 

targetディレクトリ配下に「helloworld.war」ができますので、それを右クリックして「Debug on Tomcat Server」をクリックします。

 

Hello.javaを開いて、任意の地点にブレークポイントを打ちます。

 

「https://127.0.0.1:8080/helloworld/hello」にブラウザでアクセスします。すると先程ブレークポイントを打ったところで止まります!!ステキーΣ(゚∀゚ノ)ノキャー

 

ちなみに補足ではございますが、TomcatのOfficial Dockerイメージは、CMDコマンドでTomcatの起動を行っています。今回、Tomcat for Javaプラグインで8080ポートで起動するので、CMDコマンドで起動するのとかぶったりしないかなと思ったらそうはなりませんでした。Remote Developmentでコンテナ起動するときは、おそらくdocker runコマンドの引数に以下のようにwhileループを実行するようなShellを指定して、CMDコマンドを上書きして、Tomcat起動しないみたいです。

root@571b2c2c20f0:/opt/project# ps ax
  PID TTY      STAT   TIME COMMAND
    1 ?        Ss     0:00 /bin/sh -c echo Container started ;  while sleep 1; do :; done
    7 ?        Ss     0:00 /bin/sh

まとめ

すごいですよね。Visual Studio Code + Docker + Remote Developmentの組み合わせは最強です。「Visual Studio CodeのRemote DevelopmentとDockerで快適な開発環境をゲット」の記事でPHPのデバッグにおいても大活躍でした。

TomcatでWebアプリ開発するときのデファクトスタンダートであるEclipseはかなり高機能ですが、もっとお手軽にJavaで開発したいときに、Visual Studio Codeは十分に選択肢になりえるのではないでしょうか?

しかもローカルPCに何もインストールする必要がなく、全てDocker上に環境をもたせるなんて、なんかイマドキでナウいですよね。

ということで、ワタクシ、ますますVisual Studio Codeが大好きになりました。No Visual Studio Code, No Life!!

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

3人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です