【脱Eclipse】Visual Studio CodeによるJavaのホットデプロイ環境構築

はじめに

Javaの学習でeclipseを使っていたのですが、これが重いこと重いこと・・・。調べてみると重いのがデフォみたいですが、VSCode慣れしてる自分からするとストレスがすごかった・・・多機能なんですが耐えられなくなったので今回VSCode移行することにしました。

この記事で出来るようになること

  • Visuali Studio Code による Java開発環境が準備できるようになる
  • 準備したJava開発環境で "Hello World" が表示できるようになる
  • ホットデプロイ環境が準備できるようになる

必要となる知識

  • Linux OS の基本的操作(CUI全般, インストール, ssh, 公開鍵暗号方式)
  • Visual Studio Code の基本的な操作知識
  • Javaのプロミング知識
  • dockerの基本的な知識

準備する環境について

ローカル環境(Windows、Mac等)にコンテナ立てて、そこでホットデプロイ環境を構築する記事はありますが、今回はVM上にコンテナ環境を構築、そこにWindowsなどからアクセスしてコード開発できる環境を作ります。

  • Windows10
  • Visual Studio Code
  • VirtualBOX
  • Ubuntu20.04
  • Docker20.10
  • Docker-Compose 1.29
  • Java11
  • Tomcat9

大体の環境としては上記になります。ほかにもVSCodeに拡張機能を入れたりしますが、それは後ほど。全体図としては下記になります。

流れとしてはローカルVM内にJava・Maven・Tomcatが入ったコンテナを用意し、そこに対してVSCodeでログイン、コンテナ内でJavaアプリを作成しMavenでデプロイ、デプロイしたものをTomcatで公開する、という感じです。

なお今回の記事ではdocker/docker-composeの導入やVSCodeでのSSH接続については細かく説明していません。都度リンクを記載しているのでそちらをご確認ください。

Visual Studio Code拡張モジュールの追加

それではやっていきましょう。

まずはVSCodeに必要な拡張機能をインストールしていきます。

➀Tomcat for Javaのインストール

Ctrl + Shift + Xを押して拡張機能を開き、「tomcat」と入力、出てきた「Tomcat for Java」をインストールします。

②Extension Pack for Javaのインストール

同じく拡張機能にて「extension pack」と入力、出てきた「Extension Pack for Java」をインストールします。

③Remote-Containerのインストール

拡張機能にて「remote」と入力、出てきた「Remote-Container」をインストールします。

④Remote-SSHをインストール

拡張機能にて「remote」と入力、出てきた「Remote-SSH」をインストールします。

これで必要な拡張機能はインストールできました。

コンテナの準備

次にVM(Ubuntu)内で動作させるコンテナの準備をします。なお必要なDocker/Docker-Composeのインストールについては省略します。このあたりは公式やコミュニティなどに記載があるのでそちらを参照されるのが良いと思います。

Dockerインストール

https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04-ja

Docker-Composeインストール

https://matsuand.github.io/docs.docker.jp.onthefly/compose/install/

まずはVSCodeで作業対象のVMにログインしましょう。

VMへのアクセスは通常の公開鍵認証でのSSHアクセスとそう変わりません。なので端末の.ssh/configファイルに接続先VMの情報を記載し、端末の公開鍵をVMに登録しておきましょう。

VSCodeでの作業としては左メニューのリモートエクスプローラーを押し、上のプルダウンから「SSH Targets」を選択、接続先VM(今回はIPで表示してます)の右にあるマークをクリックするとVSCodeで対象VMに接続できます。

https://code.visualstudio.com/docs/remote/ssh

ログイン出来たら作業用のディレクトリを作成します。今回は下記のようなディレクトリ構成になります。

java_product
├── .devcontainer
│   └── devcontainer.json
├── development
│   └── Dockerfile
├── docker-compose.yml
└── java

ディレクトリ配下の「devcontainer.json」、「Dockerfile」、「docker-compose.yml」はこれから作成していくので今は無くても大丈夫です。

構成説明及び必要ファイルの作成

➀developmentディレクトリ

ここにはDockerfileを作成して配置します。ディレクトリをわざわざ準備する必要も実はないんですが、今後必要なファイルが増えたときに管理を楽にするために用意してます。作成するDockerfileは下記になります

# openjdk11のイメージを利用。
FROM openjdk:11
 
# ローカルのソースコードをマウントするためのディレクトリを作成。
RUN mkdir /opt/project
 
# tomcatをダウンロードして/usr/localに配置、mavenもインストールしておく。
RUN apt-get update && \
    apt-get -y install maven && \
    curl -sL https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.58/bin/apache-tomcat-9.0.58.tar.gz \
    | tar zx -C /usr/local

イメージはopenjdkイメージを利用し、コンテナで利用するTomcatはダウンロードしてきて指定したディレクトリに解凍しておきます。

コンテナイメージとしてtomcat-jdkのイメージはありますが、今回それは利用せず上記の形にしています。理由としてはtomcatイメージを利用するとtomcatを再起動したいときにコンテナごと再起動してしまうのと、上記の形であれば別バージョンのTomcatを利用したいときにすぐに切り替えられるからです。

②.devcontainerディレクトリ

Remote-Containerで利用する設定ファイル「devcontainer.json」を配置します。

今回コンテナの起動はサーバコンソールでは行わず、VSCodeで起動させます。その時に必要な設定を記載します。

{
  // 任意の名前でOK。
  "name": "java_project",
 
  // コンテナを作成するためのdocker-compose.ymlを指定。
  "dockerComposeFile": [
		"../docker-compose.yml"
  ],
 
  // port転送設定。
  "forwardPorts": [8080],
  //docker-compose.ymlのservicesと合わせる
  "service": "tomcat", 
  // コンテナログインした際のカレントディレクトリ指定。
  "workspaceFolder": "/opt/project",
 
  // コンテナが生成されたときに、コンテナ側にインストールするVSCodeの拡張機能。
  // 今回はJava Extension PackとTomcat for Javaをインストール。
  "extensions": [
    "adashen.vscode-tomcat",
    "vscjava.vscode-java-pack"
  ],
  "settings": {
	 "java.debug.settings.hotCodeReplace": "auto" 
  }
}

「devcontainer.json」の内容は今回上記になります。

詳細に関しては公式のドキュメントをご覧ください。

https://code.visualstudio.com/docs/remote/devcontainerjson-reference

extensionsでVSCodeの拡張機能を指定できます。これをすることによってコンテナ内でもVSCodeの拡張機能を利用してビルドやTomcatの起動などができるようになります。

③docker-compose.yml

次にdocker-compose.ymlを作成します。

version: '3.5'
services:
  tomcat:
    container_name: tomcat
    build: ./development
    volumes:
      - "data:/opt/project"
    environment:
      - TZ=Asia/Tokyo
    network_mode: host
    tty: true
volumes:
  data:
    driver: local
    driver_opts:
      type: none
      device: ${PWD}/java
      o: bind

特別なことはしていませんが、同階層にあるjavaディレクトリをコンテナ内の/opt/projectディレクトリにマウントしています。こうすることによってコンテナ内で行った作業が消えないようにします。

なのでjavaディレクトリは現在は空ですが、この後の作業によってjavaのプロジェクトファイルが配置されます。

ここまでできたら下記のようになっているはずです。

コンテナ起動

ここからが本番です。

Remote-Containerを使用してコンテナを起動してみましょう。

Ctrl + Shift + Pを押し、「remote-containers」と入力してみましょう。そうすると「Remote-Containers:Open Folder in Container...」があるので、そちらをクリックします。

そうするとディレクトリの場所を指定するよう言われます。ここでは「.devcontainerフォルダ」があるディレクトリを指定し「OK」を押します。

初回の起動は時間がかかりますが、以下の画面が表示されればOKです。

Mavenでのwebapp作成

環境準備は整ったので実際に試してみましょう。

まずはMavenでjavaプロジェクトを作成します。

左のプロジェクトのところで右クリック、「Create Maven Project」を押してwebappを作成します。

必要な情報を入力するよう求められます、今回は検証するだけなのですべてデフォルトにします。

最後に、コンソール画面で入力が求められるので「1.0-SNAPSHOT」は空欄、最後に「Y」を入力して完了です。そうすると左メニューにプロジェクトが表示されます。

ではこのままビルドしてTomcatで表示させてみます。

Tomcat起動

コンテナにVSCodeの拡張機能のTomcat for Javaを入れているのでそれを利用してTomcatを起動させます。

左メニューに「TOMCAT SERVERS」がいるので✙ボタンを押して、Dockerfileで取得したtomcatのディレクトリを指定します。

追加されたら右クリック⇒Startを押して起動してみましょう。表示が緑になれば問題なく起動しています。

ビルド・Web表示

それでは先ほど作成したプロジェクトをビルドしてWebに表示してみましょう。

左メニューの下に「MAVEN」があります。そこに先ほど作成したdemoプロジェクトが表示されているので右クリック⇒Packageを選択します。

Packageが完了するとBUILD SUCCESSと表示され、targetディレクトリとその中にwarファイルが作成されます。

では表示させてみましょう、warファイルを右クリックし「Run on Tomcat Server」を選択、Tomcatにwarファイルが設置されるので、右クリック「Open in Browser」でHello Worldが表示されることを確認します。

これでビルドと公開までの手順確認ができました。では最後にホットデプロイを確認してみましょう。

ホットデプロイ

VSCodeではJSPなどはこれからやる方法(デバック起動)でも反映されません。なのでJSPなどは更新した場合都度Packageが必要になります。今回はホットデプロイの確認としてサーブレットを作成しそこにloggerを追加する方法で確認してみます。

Mavenでwebappを作成するとjava作成に必要なディレクトリが作成されないのでディレクトリを作成したうえでサーブレットとlogger用のlogback.xmlを作成します。

demo
├── src
│   └── main
│         ├── java ★
│         │     ├── com ★
│         │     │       └── example ★
│         │     │              └── servlet ★
│         │     │                     └── MyServlet.java ★
│         │     └──logback.xml ★
│         └── webapp
│                ├── WEB-INF
│                │      └── web.xml
│                └── index.jsp
├── target
│     └── ・・・
└── pom.xml

ディレクトリ構造は上記のようになります。★が新規で作成したフォルダおよびファイルです。

MyServlet.javaの中身は下記にです。

package com.example.servlet;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class MyServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;
  //private static final Logger logger = LoggerFactory.getLogger(MyServlet.class);

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

  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
   *      response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
      //logger.info("Debug OK");
      response.getWriter().append("Hello Java!");
  }

  /**
   * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
   *      response)
   */
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
      doGet(request, response);
  }

}

logback.xmlの中身は下記になります。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy/MM/dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

このままだと下図のように赤いエラーが表示されます。これは必要なjarファイルが無いからですね。なのでpom.xmlを修正して必要なモジュールをインストールします。

pom.xmlのpropertiesとdependenciesを以下のように変更します。

・・・・・ 
 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>11</java.version>
		<maven.compiler.target>${java.version}</maven.compiler.target>
		<maven.compiler.source>${java.version}</maven.compiler.source>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
			<groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
		</dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.25</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.10</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.2.10</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.6.1</version>
    </dependency>
  </dependencies>
・・・・・

propertiesですが、今回サーバに入れているjavaは11なので、デフォルト値から変更しています。

dependenciesについては今回利用するモジュールを追加しました。

web.xmlも更新しましょう。

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

<web-app>
  <display-name>My Servlet Web Application</display-name>

  <servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>com.example.servlet.MyServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/my-servlet/*</url-pattern>
  </servlet-mapping>
</web-app>

これで準備が整いました。まずはPackageをしてwarファイルを作成します。

作成されたwarファイルを右クリックして「Debug on Tomcat Server」を押します。

アクセスします。今回のアクセス先は「http://localhost:8081/demo/my-servlet/」になります。

この画像のように表示されたらOKです。うまく表示されない場合は一度tomcatサーバにあるwarファイルを削除したり再起動してみてください。

さて、ではMyServlet.javaでコメントアウトしていたところを修正します。

package com.example.servlet;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class MyServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;
  private static final Logger logger = LoggerFactory.getLogger(MyServlet.class); //★

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

  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
   *      response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
      logger.info("Debug OK"); //★
      response.getWriter().append("Hello Java!");
  }

  /**
   * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
   *      response)
   */
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
      doGet(request, response);
  }

}

★がついているところを修正(コメントアウトを外す)しました。

それではファイルを保存してそのまま「http://localhost:8081/demo/my-servlet/」にアクセスするか、更新ボタンを押してみましょう。そうするとVSCodeの出力に

先ほど追加したloggerの出力がでてきました!

終わりに

今回は可能な限りVSCodeでJavaが快適に開発できる環境を構築してみました。Eclipseには劣りますが個人的には軽いVSCodeのほうがやはりやりやすいですね。

今回はDockerを利用しているのでVSCodeとローカルVM(or Server)さえあれば同じ環境が作れるのが魅力的かなと思います。

この記事が気に入ったら
フォローしよう

最新情報をお届けします

Twitterでフォローしよう

おすすめの記事