20201025のJavaに関する記事は11件です。

Javaで自作アノテーションを作成し値を取得する方法

Javaで自作アノテーションを作成し、アノテーションの値を取得するサンプルプログラムを紹介します。

環境

  • jdk1.8.0_231
C:\>java -version
java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)

C:\>

1. 自作アノテーションを作成

自作アノテーションのサンプルです。ここではメンバに「name」と「value」を持たせています。

MyAnnotation.java
package test01;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)     // (1) @Retantionでアノテーション情報をどの段階まで保持するかを制御
@Target({                               // (2) @Targetどのようなタイプの要素に対して、そのアノテーションを付加できるのかを制御
    ElementType.TYPE,
    ElementType.FIELD,
    ElementType.CONSTRUCTOR,
    ElementType.METHOD
})
public @interface MyAnnotation {        // (3) MyAnnotationを作成
    String name();
    int value() default 0;
}

(1) の@Retentionでアノテーション情報をどの段階まで保持するかを指定します。

@Retentionには主に以下を設定することができます。

説明
RetentionPolicy.SOURCE アノテーションはソースレベルでのみ保持され、コンパイラによって無視されます。
RetentionPolicy.CLASS アノテーションはコンパイル時にコンパイラーによって保持されますが、Java仮想マシン(JVM)によって無視されます。
RetentionPolicy.RUNTIME アノテーションはJVMによって保持されるため、ランタイム環境で使用できます。

ここではRetentionPolicy.RUNTIMEを指定します。

(2) の@Targetでアノテーションを適用可能な場所を指定します。

@Targetには主に以下を設定することができます。

説明
ElementType.TYPE クラス、インターフェース、アノテーション、enum型に適用可能にします。
ElementType.FIELD フィールドに適用可能にします。
ElementType.CONSTRUCTOR コンストラクタに適用可能にします。
ElementType.METHOD メソッドに適用可能にします。

ここでは@TargetElementType.TYPEElementType.FIELDElementType.CONSTRUCTORElementType.METHODを指定します。

(3) でMyAnnotationの名前でアノテーションを作成します。メンバにnamevalueを設定します。

2. クラスにアノテーションを付与

Sampleクラスのクラス、フィールド、コンストラクタ、メソッドに自作のMyAnnotationを付与します。

Sample.java
package test01;

@MyAnnotation(name="class", value=100)              // (1) クラスにMyAnnotationを付与
public class Sample {

    @MyAnnotation(name="field", value=200)          // (2) フィールドにMyAnnotationを付与
    private String name;

    @MyAnnotation(name="constructor", value=300)    // (3) コンストラクタにMyAnnotationを付与
    public Sample() {}

    @MyAnnotation(name="method", value=400)         // (4) メソッドにMyAnnotationを付与
    public void execute() {
        System.out.println("execute");
    }
}

(1) クラスに自作アノテーション(MyAnnotation)を付与します。
(2) フィールドに自作アノテーション(MyAnnotation)を付与します。
(3) コンストラクタに自作アノテーション(MyAnnotation)を付与します。
(4) メソッドに自作アノテーション(MyAnnotation)を付与します。

3. アノテーションを取得して参照

Main.java
package test01;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Main {

    public static void main(String[] args)
            throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, SecurityException {

        // (1) クラスについているアノテーション(MyAnnotation)を取得
        Class<?> clazz = Class.forName("test01.Sample");
        MyAnnotation annoClass = (MyAnnotation) clazz.getAnnotation(MyAnnotation.class);
        System.out.println("class annotation : name=" + annoClass.name() + ", value=" + annoClass.value());

        // (2) フィールドについているアノテーション(MyAnnotation)を取得
        Field field = clazz.getDeclaredField("name");
        MyAnnotation annoField = (MyAnnotation) field.getAnnotation(MyAnnotation.class);
        System.out.println("field annotation : name=" + annoField.name() + ", value=" + annoField.value());

        // (3) コンストラクタについているアノテーション(MyAnnotation)を取得
        Constructor<?> cons = clazz.getConstructor();
        MyAnnotation annoCons = (MyAnnotation) cons.getAnnotation(MyAnnotation.class);
        System.out.println("constructor annotation : name=" + annoCons.name() + ", value=" + annoCons.value());

        // (4) メソッドについているアノテーション(MyAnnotation)を取得
        Method method = clazz.getMethod("execute");
        MyAnnotation annoMethod = (MyAnnotation) method.getAnnotation(MyAnnotation.class);
        System.out.println("method annotation : name=" + annoMethod.name() + ", value=" + annoMethod.value());

    }
}

(1) Sampleクラスに付いているアノテーション(MyAnnotation)の内容を取得し表示します。
(2) Sampleクラスのフィールドnameに付いているアノテーション(MyAnnotation)の内容を取得し表示します。
(3) Sampleクラスのコンストラクタに付いているアノテーション(MyAnnotation)の内容を取得し表示します。
(4) Sampleクラスのexecuteメソッドに付いているアノテーション(MyAnnotation)の内容を取得し表示します。

参考

Lesson: Annotations (The Java™ Tutorials < Learning the Java Language)


以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】楽曲ファイルからタグ情報を取得する

はじめに

楽曲ファイルのタグ情報(楽曲名、アルバム名、アーティスト名など)は各種メディアプレイヤーから簡単に編集が可能です。
特にiTunesでは Gracenote を使ってCDからのインポートと同時にタグ情報の自動取得まで行ってくれてとても便利です。
しかし、中には取得してきたタグ情報が 自分好みじゃない 場合もあるかと思います。
例をあげると

  • 楽曲名がCD表記と異なる
  • アーティスト名が表記揺れしている
  • ジャンルが異なる(ここは個人の主観もあると思いますが...)

などなど...
これらを1曲ずつポチポチ編集するのは面倒なので、システムからタグ情報を操作できればいいなーと常々思ってました。(そのためには上記の要件を満たすCDDBも必要になりますが...)
今回は システムからタグ情報を操作する のうち タグ情報を取得する 処理に関して、 Java言語SpringBoot の組み合わせで JAudioTagger というライブラリを検証した結果を備忘録として残します。

pom.xmlの編集

pom.xmldependencies に以下の依存関係を追加します

<dependency>
  <groupId>org</groupId>
  <artifactId>jaudiotagger</artifactId>
  <version>2.0.3</version>
</dependency>

タグ情報の読み込み

まずはiTunesにインポートされている適当な曲のタグ情報を確認します
tag_ss.png

それでは実際にJavaコードからタグ情報を読み込んでみます(コードは以下を参照)

AudioFile audioFile = AudioFileIO.read(楽曲ファイルを読み込んだファイルオブジェクト);
Tag tag = audioFile.getTag();

System.out.println(tag.getFirst(FieldKey.TITLE));
System.out.println(tag.getFirst(FieldKey.ARTIST));
System.out.println(tag.getFirst(FieldKey.ALBUM_ARTIST));
System.out.println(tag.getFirst(FieldKey.ALBUM));
System.out.println(tag.getFirst(FieldKey.YEAR));
System.out.println(tag.getFirst(FieldKey.COMPOSER));
System.out.println(tag.getFirst(FieldKey.GENRE));

出力結果は以下となりました。

誰がその鐘を鳴らすのか?
欅坂46
欅坂46
誰がその鐘を鳴らすのか?
2020
辻村有記
J-POP

ちなみに FieldKey は以下のようなものが取得できるようです

FieldKey 取得できるタグ情報
TITLE 楽曲名
TITLE_SORT 楽曲名(よみ)
ARTIST アーティスト名
ARTIST_SORT アーティスト名(よみ)
ALBUM_ARTIST アルバムアーティスト
ALBUM_ARTIST_SORT アルバムアーティスト(よみ)
ALBUM アルバム名
ALBUM_SORT アルバム名(よみ)
YEAR アルバム発売年
COMPOSER 作曲者
COMPOSER_SORT 作曲者(よみ)
GENRE ジャンル

この他にも色々なフィールドが取得できるようです(参考:マッピング表)

おわりに

今回は タグ情報の取得 を検証しましたが、次は タグ情報の書き込み も検証してみたいと思います。
また、Java 以外の言語でのタグ情報の操作も、時間がある際に検証してみたいと思います。
最後になりますが、サブスクが主流となりつつある昨今、ローカルの楽曲ファイルを操作すること自体がナンセンスかもしれません(笑)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaでTODOアプリを制作しよう12 使われていないHttpMethodでリクエストが来た時の処理・サーバー内でエラーが起きた時の処理

こんにちは。

前回までに引き続き例外処理の実装を進めていきましょう!

TODOアプリ作成リンク集

1: [超基礎の理解] MVCの簡単な説明
2: [雛形を用意する] Spring Initializrで雛形を作ってHello worldしたい
3: [MySQLとの接続・設定・データの表示] MySQLに仮のデータを保存 -> 全取得 -> topに表示する
4: [POST機能] 投稿機能の実装
5: [PATCH機能] TODOの表示を切り替える
6: [JpaRepositoryの簡単な使い方] 検索機能の実装
7: [Thymeleaf テンプレートフラグメントで共通化] Headerの作成
8: [PUT機能] 編集機能の実装
9: [微調整]TODOの表示を作成日時が新しい順にソートする + 期日のデフォルトを今日の日付にする
10: [springで例外処理] 例外処理についての簡単なまとめ
11: [springで例外処理] 存在しないIDのTODOにアクセスした時の例外処理
12: [springで例外処理] 使われていないHttpMethodでリクエストが来た時の処理・サーバー内でエラーが起きた時の処理

HttpMethodに関する簡単な説明

HTTPメソッドって一体何?と思われる方も多いと思うので今回はまずざっくりですがどういうものなのかを説明してみようと思います。

ウェブサイトにアクセスするとはどういう事なのかを軽く理解する

まず理解して頂きたいのが

インターネットのサイトにアクセスする = ネット上にあるサーバーにアクセスする

ということです。

このサーバー内にはHPを表示させたり色々な機能(例えばTODO機能とか)が置いてあり、アクセスされれば製作者の想定通りに動くはずです。

ちなみに現在作成しているTODOアプリはウェブ上には公開されていませんが、私たちのローカル上に仮のサーバーを作っているので、アプリをRunするとlocalhost:8080にアクセスできるようになっているのです。

URLとHttpMethod

さてサーバーにアクセスする事によってHPを利用できるということが分かりましたが

アクセスする際には何が必要でしょうか?

まずはそのサイトのアドレスが必要になりますね!

サイトが家みたいなものだとしたらアドレスは住所(番地)になるでしょう!

普通にHPを利用するだけでしたらアドレスだけを知っていれば良いのですが、実際にウェブ製作者としてHPを作るとなると、もう一つの概念HttpMethodを理解しなければなりません。

実はサイトにアクセスする際にはHttpMethodというものをつかってアクセスをリクエストしています。

サイトのURL + HttpMethodの同時をリクエストすることで、サイト側はユーザーがどのページにアクセスしたいのかを判別しています!

例えば以下の例を見てみます。

com/example/todo/TodoController.java
@Controller
public class TodoController {

    @GetMapping("/top")
    public String top(Model model){
        return "top";
    }

これはTODOアプリのトップページを表示する部分ですが注目したいのが

@GetMapping("/top")の部分です。

このアノテーションはユーザーが

サイトのURL/topGETメソッドでリクエストしてきた時に行いたい処理というのを明記する役割があります。

よってユーザーがそのようなリクエストをしてきた場合は関数topが走るというわけです。

サイトが家でアドレスが住所であればHttpMethodは鍵のようなものかもしれません。

家の中のトップページを表示させる部屋に入るのに必要な鍵といった感じです。

同じURLでもHttpMethodを変えてアクセスすることもできる

例えば下記の例をみてましょう

com/example/todo/TodoController.java
@Controller
public class TodoController {

    @GetMapping("/top")
    public String top(Model model){
        return "top";
    }

    @PostMapping("/top")
    public String top(Model model){
        //投稿処理
    }

POSTメソッドで/topにアクセスした時の処理を追加してみました。

アクセスするURL自体は同じですがリクエスト時のHTTPメソッドを変更することで違う処理を実現することができます。

様々なHttpMethod

さてHttpMethodにはいろいろな種類がありますが、代表的な物とその役割をまとめてみます。

GET
サイト内のリソースを取得する際に使われる(例:トップページにDBから取得したTODO(リソース)を表示する)
POST
何かを投稿したりする際に使われる(例:TODO投稿時」)
PUT
データの内容を置換する(例:ファイルをアップロードして丸々中身を変える)
PATCH
データの一部もしくは全体を更新、修正して保存する(例:TODO編集時」)

使われていないHttpMethodでリクエストされた時の例外処理

com/example/todo/exception/TodoControllerAdvice.java
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public String badMethod() {
        log.warn("Bad Request");
        return "error/405.html";
    }
templates/error/405.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>405</title>
</head>
<body>
405!
</body>
</html>

上記2つを追加してあげましょう。

ControllerAdviceについては前回の記事を見れば書き方がわかると思います。

ここではHttpRequestMethodNotSupportedException(存在しないHttpMethodのリクエストが投げられた時に起こる例外クラスです。)が起きた時に405.htmlを表示するようにしています。

せっかくなので実際に存在しないHttpMethodをCurlコマンドを使って投げてみましょう!

Todoアプリを起動してターミナルで

$ curl -X POST "http://localhost:8080/top"

と打ちます。これは本来はGETでしか処理を実装していない/topに対してPOSTでアクセスリクエストを投げています。

すると結果は
Screen Shot 0002-10-25 at 17.01.14.png

この様になるはずです!想定通り405が表示されていることがわかります。

ついでサーバー内でエラーが起きた時の設定もしておく

com/example/todo/exception/TodoControllerAdvice.java
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public String otherErrors() {
        log.error("Something went wrong");
        return "error/500.html";
    }
templates/error/500.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>500</title>
</head>
<body>
500!
</body>
</html>

これらを追加することでExceptionクラスが起きた際に500エラーのページを表示できるようになりました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】Thymeleaf基本 (SpringBoot)

Thymeleafとは

  • SpringBootで標準で使われるテンプレートエンジン
  • テンプレートエンジンを使うことでWebアプリケーション(MPA)を作るのに必要な機能が追加される
  • 例えばHTMLファイルの中に[[]]でJavaの変数名[[${modelValue}]]を書くことができる
  • これで画面が表示される時に、HTMLファイルがテンプレートとなって、Placefolder(=置き換わる場所)をSpringフレームワークが書き換えてくれる
  • テンプレートの機能を果たすものをThymeleafという timeleaf.png

使用方法

  • build.gradleファイルのdependenciesにThymeleafが定義がされていることを確認
dependencies {
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
}

テンプレートファイルの呼び出し

  • デフォルト設定では/resources/templates/【Controller の戻り値】.html 配下にテンプレートファイルを記述
  • 以下の例はMain.javaでHelloControllerを呼び出し、クラスパス以下のテンプレートファイルが探索されsrc/main/resources/templates/hello.html がテンプレートファイルとして利用される。
main.java
package sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}
HelloController.java
package sample.thymeleaf.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", "Hello Thymeleaf!!");
        return "hello";
    }
}
hello.html
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8" />
        <title>Hello Thymeleaf</title>
    </head>
    <body>
        <h1 th:text="${message}"></h1>
    </body>
</html>

テンプレートファイルでテキストを埋め込み

  • th:text=【出力する値】そのタグのテキスト要素を出力
  • <h1 th:text="'hello world'"></h1>
  • <h1>[['hello world!!']]</h1> で直接テンプレート上に値を出力することもできる
HelloController.java
package sample.thymeleaf.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}
hello.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8" />
        <title>Hello Thymeleaf</title>
    </head>
    <body>
        <h1 th:text="'hello world'"></h1>
    </body>
</html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Eclipse Pleiades All in One を使って Tomcat プロジェクトを作成する

はじめに

  • Eclipse Pleiades All in One を使って Tomcat プロジェクト(Java Servlet)を作成する方法についてまとめました。Eclipse Pleiades All in One は Eclipse の日本語化対応がされており、JDK や TOMCAT など Java Servlet を作成するために必要なソフトウェアがパッケージ化されているので環境構築がしやすい点がメリットです。

概要

  • Java Servlet とは、Java で実装された WEB アプリケーションにおいて、動的 WEB ページを生成するプログラムです。WEB アプリケーションは、サーバ・クライアントシステムになっており、以下の図のように、クライアントがサーバにリクエストを送信し、サーバがクライアントにレスポンスを返して動作します。

  • Java Servlet は単体で動作するのではなく、サーブレットコンテナと呼ばれるソフトウェアと一緒に動作します。Tomcat とは、このサーブレットコンテナに該当します。WEB ブラウザから WEB サーバにリクエストを送信すると、Tomcat がこのリクエストを受信します。サーブレットは、リクエストの種類に応じた動的 WEB ページを生成し、Tomcat は、この動的 WEB ページをWEB ブラウザにレスポンスとして送信します。WEB ブラウザには、この動的 WEB ページをが表示されます。

動作環境

  • Windows 10 (64bit)
  • Eclipse Pleiades All in One(Windows 64bit Full Edition Java Version 2020-06)
  • Tomcat 9

開発環境構築:Eclipse インストール

Eclipse Pleiades All in One は、Eclipse の日本語対応がされており、JDK や Tomcat など Java Servlet 開発に必要なソフトウェアが含まれています。
まず、Eclipse Pleiades All in One をダウンロードして起動し、Tomcaat Plagin をインストールします。

  1. 以下の Pleiades(Java アプリを日本語化するツール)のサイトに移動し、「Eclipse 2020」をクリックします。
    ⇒「Download」ボタンの一覧が並んだページに移動します。
    https://mergedoc.osdn.jp/

  2. 「Windows 64bit」、「Full Edition」、「Java」の組み合わせ上の「Download」ボタンをクリックします。
    ⇒pleiades-2020-06-java-win-64bit-jre_20200702.zip がダウンロードされます。

  3. ダウンロード完了したら、任意のフォルダに解凍し、eclipse\eclipse.exe をダブルクリックして、Eclipseを起動します。
    (起動時に、workspaceフォルダのパスの入力が必要ですが、デフォルト設定のままにします。)

  4. Eclipse のメインメニュー「ヘルプ」>「Eclipse マーケットプレース」を実行して、「Eclipse マーケットプレース」ダイアログを起動します。

  5. 「検索」欄に、「Tomcat」と入力して、「Go」ボタンを押します。

  6. 「Eclipse Tomcat Plugin 9.1.4」を選択して、「インストール」ボタンを押します。

  7. 「使用条件の条項に同意します」を選択して「完了」ボタンを押す。

  8. 証明証ダイアログ「これらの証明証を信頼しますか?」が表示されたら、証明書発行元にチェックして「選択を受け入れる」ボタンを押します。

  9. Eclipse を再起動すると、Eclipseのメインメニューのアイコンに Tomcat のアイコンがメニューに表示されます。

Eclipse 初期設定

プロジェクト作成の前に Eclipse の初期設定を行います。最初に一度設定しておけば次回からは設定不要です。

  1. メインメニュー「ウィンドウ」>「設定」を実行して、「設定」ダイアログを起動します。

  2. まず、Tomcat の設定をします。左側のツリーから「Tomcat」を選択します。

  3. 「Tomcat」ホームに Tomcat のフォルダを設定します。
    (pleiades-2020-06-java-win-64bit-jre_20200702.zip を解凍したフォルダ下の「tomcat\9」を設定します。)

  4. 「コンテキスト宣言モード」で「Server.xml」をチェックし、「設定ファイル」欄に Server.xml のパスを設定します。
    (pleiades-2020-06-java-win-64bit-jre_20200702.zip を解凍したフォルダ下の「tomcat\9\conf\server.xml」を設定します。)

  5. 「適用」ボタンを押します。

  6. 次に、デプロイする環境が Linux であることを想定して、Linux 上で動作するように設定します。左側のツリーから「一般」>「ワークスペース」を選択します。

  7. 「テキスト・ファイル・エンコード」で「その他」をチェックし、「UTF-8」を選択します。

  8. 「新規テキスト・ファイルの行区切り文字」で「その他」をチェックし、「Unix」を選択します。

  9. 「適用」ボタンを押します。

  10. 「適用して閉じる」ボタンを押します。

プロジェクト作成

プロジェクトの新規作成時と Server.xml の設定を行います。

  1. メインメニュー「ファイル」>「新規」>「プロジェクト」を実行して、「新規プロジェクト」ダイアログを起動します。

  2. ツリー内の「Java」>「Tomcatプロジェクト」ノードをクリックします。

  3. 「プロジェクト名」を入力して、「次へ」ボタンを押します。

  4. 「コンテキスト名」欄のパスを確認して、「Finish」ボタンを押します。(「コンテキスト名」欄のパスが、WEB サーバにアクセスするときの URL パスのルート部分になります。)
    http://localhost:8080/{「コンテキスト名」欄のパス}

  5. Tomcat のインストールフォルダ下の「conf\server.xml」に以下の行が追加されていることを確認します。この行が Server.xml に登録されていない場合、WEB サーバにアクセスしても、WEB 画面は表示されません。
    (【例】C:\apache\apache-tomcat-9.0.37\conf\server.xml)

    <Context path="{プロジェクト名}" reloadable="true" docBase="{プロジェクトのパス}" workDir="{プロジェクトのパス}\\work" />
    
  6. デフォルトでは、8080 番ポートを使用する設定になっています。8080 番ポート以外に設定したい場合は、以下の部分を変更します。

    <Connector port="8080" protocol="HTTP/1.1"      ★"8080"の部分を設定したいポート番号に変更
    connectionTimeout="20000" 
    redirectPort="8443" useBodyEncodingForURI="true" />
    

サーブレット作成

サーブレットの処理を実装します。

  1. 「パッケージ・エクスプローラー」上のツリーのトップノードを選択して右クリックし、ポップアップメニュー「新規」>「クラス」をクリックします。

  2. 「名前」欄にクラス名、「スーパークラス」欄に「javax.servlet.http.HttpServlet」を入力して「完了」ボタンを押します。
    (ここでは、「名前」欄に「SampleServlet」と入力し、「名前」欄と「スーパークラス」欄以外の設定はデフォルトのままとします。)
    ⇒「パッケージ・エクスプローラー」上のツリーに「sample」>「SampleServlet.java」ノードが追加されます。

    SampleServlet.java
    package sample;
    
    import javax.servlet.http.HttpServlet;
    
    public class SampleServlet extends HttpServlet {
    
    }
    
  3. 「パッケージ・エクスプローラー」上のツリーノード「SampleServlet.java」を選択して、メインメニュー「ソース」>「メソッドのオーバーライド/実装」をクリックし、「メソッドのオーバーライド/実装」ダイアログを起動します。

  4. 「HttpServlet」>「doGet」をチェックして「OK」ボタンを押して、doGet() と doPost() を生成します。doGet() は GET メソッドを受信したときに動作します。GET メソッドは、クライアントの WEB ブラウザから、WEB サーバにアクセスがあったときに動作します。また、doPost() は POST メソッドを受信したときに動作します。POST メソッドは、クライアントの WEB ブラウザのフォームから WEB サーバにデータが送信されたときに動作します。

    SampleServlet.java
    package sample;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class SampleServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // TODO 自動生成されたメソッド・スタブ
            super.doGet(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // TODO 自動生成されたメソッド・スタブ
            super.doPost(req, resp);
        }
    
    }
    
  5. 以下の import 処理を追加します。

    SampleServlet.java
    package sample;
    
    import java.io.IOException;
    import java.io.PrintWriter;     // 追加
    
  6. doGet() の実装を以下のように変更します。クライアントから WEB サーバにアクセスしたときに、クライアントの WEB ブラウザに表示される WEB ページを作成しています。

    SampleServlet.java
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // TODO 自動生成されたメソッド・スタブ
            //super.doGet(req, resp);           // コメントアウト
    
            // ↓ ここから追加
            // 日本語の文字化け対策のため、文字エンコーディングを行う
            resp.setContentType("text/html; charset=UTF-8");
    
            // クライアントの WEB ブラウザに表示する WEB 画面を作成する
            PrintWriter out = resp.getWriter();
            out.println("<html>");
            out.println("<head>");
            out.println("<title>sample</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Hello こんにちは</h1>");
            out.println("</body>");
            out.println("</html>");
            out.close();
            // ↑ ここまで追加
        }
    

web.xml 作成

web.xml を作成します。web.xml はサーブレットコンテナの設定をするファイルです。

  1. 「パッケージ・エクスプローラー」上のツリーノード「WEB-INF」を選択して右クリックし、ポップアップメニュー「新規」>「その他」をクリックして、「新規」ダイアログを起動します。

  2. 「XML」>「XML ファイル」をクリックして、「新規 XML ファイル」ダイアログを起動します。

  3. 「ファイル名」欄に「web.xml」と入力して「完了」ボタンを押します。
    ⇒「パッケージ・エクスプローラー」上に、ツリーノード「web.xml」が追加されます。

  4. 「パッケージ・エクスプローラー」上のツリーノード「web.xml」を選択して右クリックし、ポップアップメニュー「次へ開く」>「汎用テキスト・エディター」をクリックします。

    web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    
  5. web.xml に以下のコードを追加します。

    web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    
    <!-- ↓ここから追加 -->
    <web-app>
        <servlet>
            <servlet-name>sample</servlet-name>                 <!-- Servlet name -->
            <servlet-class>sample.SampleServlet</servlet-class> <!-- Class name -->
        </servlet>
    
        <servlet-mapping>
            <servlet-name>sample</servlet-name>                 <!-- Servlet name -->
            <url-pattern>/servlet</url-pattern>                 <!-- URL pattern -->
        </servlet-mapping>
    </web-app>
    <!-- ↑ここまで追加 -->
    

ビルド

ビルドを行います。

  1. 「パッケージ・エクスプローラー」上のツリーのトップノードを選択して右クリックし、ポップアップメニュー「リフレッシュ」をクリックします。
    ⇒「パッケージ・エクスプローラー」上のツリーのファイル構成が現在の状態に自動的に更新されます。

  2. 「パッケージ・エクスプローラー」上のツリーのトップノードを選択して右クリックし、ポップアップメニュー「プロジェクトのビルド」をクリックして、ビルドします。
    ※メインメニュー「プロジェクト」>「自動的にビルド」をチェックすると、自動的にビルドされ、このポップアップメニューは消えます。

実行

ビルドができたら実行します。まずは、Eclipse を使用して、Windows 上に WEB サーバを起動して、WEB ブラウザからアクセスします。

  1. メインメニューのアイコン「Tomcat 再起動」をクリックします。

  2. ブラウザを起動して以下の URL にアクセスすると、クライアントからサーバに GET メソッドが送信され、以下のような WEB ページが表示されます。
    http://localhost:8080/sample/sevlet

デバッグ

デバッグしたい場合は、以下のようにします。

  1. 右クリックしてポップアップメニュー「ブレークポイントの切り替え」をクリックしてブレークポイントを設定し、F8 キー(実行)を押します。

  2. ブラウザを起動して以下の URL にアクセスすると、ブレークポイントを設定した場所でブレークします。また、 F6 キーでステップアウト、F5 キーでステップインできます。
    http://localhost:8080/sample/sevlet

WAR ファイル作成

デプロイ(利用可能な状態にする)するために、WAR ファイル(WEB アプリの動作に必要なファイルをまとめたもの)を作成します。

  1. メインメニュー「Project」>「Properties」をクリックして、「Properties」ダイアログを起動します。

  2. ツリーノード「Tomcat」を選択し、「WARエクスポート設定」タブを開き、「参照」ボタンを押して、「開く」ダイアログを起動します。

  3. 「ファイル名」欄に保存する WAR ファイル名を入力して、「開く」ボタンを押します。

  4. 「エクスポートするWARファイル」欄に保存する WAR ファイルのパスが反映されたら、「適用して閉じる」ボタンを押します。

  5. 「Package Explorer」上のツリーのトップノードを選択して右クリックし、「Tomcatプロジェクト」>「プロジェクト設定に従いWARファイル作成」を実行します。

  6. 「エクスポートするWARファイル」欄に設定したパスに WAR ファイルが生成されます。

プロジェクトのエクスポート

プロジェクト削除前にプロジェクトのバックアップを取得します。

  1. Tomcat を停止します。

  2. 「パッケージ・エクスプローラー」上のツリーのトップノードを選択して右クリックし、ポップアップメニュー「エクスポート」をクリックして、「エクスポート」ダイアログを起動します。

  3. 「一般」>「ファイルシステム」を選択して「次へ」ボタンを押します。

  4. プロジェクトのチェックボックスをチェックします。

  5. 「宛先ディレクトリー」欄にエクスポート先のパスを入力して「完了」ボタンを押します。

プロジェクト削除

プロジェクトの削除と Server.xml の設定削除を行います。

  1. Tomcat を停止します。

  2. 「パッケージ・エクスプローラー」上のツリーのトップノードを選択して右クリックし、ポップアップメニュー「削除」をクリックして、「リソースの削除」ダイアログを起動します。

  3. 「ディスク上からプロジェクト・コンテンツを削除(元に戻せません)」をチェックして「OK」ボタンを押します。

  4. 「プロジェクトのコンテキスト定義を Server.xml から除去」ダイアログが起動したら「OK」ボタンを押します。

プロジェクトのインポート

プロジェクトをインポートする場合はバックアップしたプロジェクトをインポートします。

  1. 『プロジェクト作成』項目にしたがって、プロジェクトを作成します。

  2. 「パッケージ・エクスプローラー」上のツリーのトップノードを選択して右クリックし、ポップアップメニュー「インスポート」をクリックして、「インスポート」ダイアログを起動します。

  3. 「一般」>「ファイルシステム」を選択して「次へ」ボタンを押します。

  4. プロジェクトのチェックボックスをチェックします。

  5. 「次のディレクトリーから」欄にインポート先のパスを入力して「完了」ボタンを押します。

  6. 「上書きしますか?」と質問されたら、「すべていいえ」を選択します。

デプロイ環境

ここから、デプロイの準備をします。ここでは、Ubuntu にデプロイします。

  • Ubuntu 20.04 LTS
  • OpenJDK 11
  • Tomcat 9

デプロイ環境構築①:OpenJDK 11 インストール

まず、Java をインストールします。

  1. OpenJDK 11 をインストールします。

    $ sudo apt-get install openjdk-11-jdk
    
  2. インストールした Java のバージョンを確認します。

    $ java --version
    openjdk 11.0.8 2020-07-14
    OpenJDK Runtime Environment (build 11.0.8+10-post-Ubuntu-0ubuntu120.04)
    OpenJDK 64-Bit Server VM (build 11.0.8+10-post-Ubuntu-0ubuntu120.04, mixed mode, sharing)
    

デプロイ環境構築②:TOMCAT インストール

次に、TOMCAT をインストールします。

  1. 下記のサイトにアクセスし、左側の「Download」項目の「Tomcat9」をクリックします。
    http://tomcat.apache.org/download-80.cgi

  2. 「Binary Distributions」内の「Core」下の「tar.gz」をクリックします。
    ⇒「apache-tomcat-9.0.37.zip」がダウンロードされます。
    https://tomcat.apache.org/download-90.cgi

  3. ダウンロードが完了したら任意のディレクトリに解凍します。ここでは、/opt に解凍します。

    $ sudo tar zxvf apache-tomcat-9.0.37.tar.gz -C /opt
    
  4. /opt に解凍されたことを確認します。

    $ ls /opt/apache-tomcat-9.0.37/
    BUILDING.txt  CONTRIBUTING.md  LICENSE  NOTICE  README.md  RELEASE-NOTES  RUNNING.txt  bin  conf  lib  logs  temp  webapps  work
    

デプロイ

デプロイ環境が準備できたら、デプロイします。

  1. tomcat の デフォルト設定では、root ユーザ以外のユーザに実行権限がないため、root ユーザになります。

    $ sudo su
    
  2. 作成した WAR ファイルを TOMCAT の webapps 下のフォルダにコピーします。

    # mv sample.war /opt/apache-tomcat-9.0.37/webapps
    
  3. TOMCAT を起動します。

    # cd /opt/apache-tomcat-9.0.37/bin
    # sh startup.sh
    
  4. WEB ブラウザを起動して以下の URL にアクセスすると、クライアントからサーバに GET メソッドが送信され、以下のような WEB ページが表示されます。
    http://{デプロイしたPCのIPアドレス}:8080/sample/servlet

  5. TOMCAT を終了します。

    # sh shutdown.sh
    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【WIP】Java変数

記事を書いた目的:Javaの基本、変数について調べ知識を整理する。

変数とは?

  • データ値を格納するための入れ物です。(コンテナとも呼ばれます)
  • Javaには、たとえば次のようなさまざまなタイプの変数があります。

  • String:"Hello "のようなテキストを格納する。文字列の値は二重引用符で囲まれている。

  • int:123 や -123 のような小数を含まない整数 (整数) を格納する。

  • float:19.99や-19.99のような小数を含む浮動小数点数を格納します。

  • char:は 'a' や 'B' のような単一の文字を格納します。文字値は単一引用符で囲まれています。

  • boolean:値を格納し、2つの状態: true または false を指定します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Migration Toolkit for Application Binariesを使ってみた

IBMから出ているMigration Toolkit for Application Binariesは無料のツールでJavaアプリケーションのバージョン間の移行やLibertyへのマイグレーションを診断してくれるツールです。診断を行うためにはJavaアプリケーションのwar/earファイルが必要です。
image.png

https://developer.ibm.com/wasdev/downloads/#asset/tools-Migration_Toolkit_for_Application_Binaries

手順は
- JDKのインストール
- このMigration Toolkitのダウンロード、インストール
- war/earファイルの準備
- 診断の実行
* 私はWindows 10の環境でやってみました。JavaがあればMac, Linuxでも同じようにできるはずです。(コマンドはPowerShellで実行)

マイグレーションオプション

このツールでサポートしているマイグレーション元、ソースのアプリケーションサーバーは

JBoss, Liberty, Liberty Core, Apache Tomcat, WebSphere, WebLogic

  • デフォルトはWebSphere Application Server traditional V8.5.5

マイグレーション先、ターゲットのアプリケーションサーバーは

Liberty, Liberty Core, WebSphere Application Server traditional V8.5.5, WebSphere Application Server traditional V9.0

  • デファルトはWebSphere Application Server Liberty

またJavaのバージョンも指定できるようで。元、ソースのJavaのバージョンは

 Java (11,14), IBM Java (5,6,7,8), Oracle Java (5,6,7,8)

ターゲットのJavaのバージョンは

 Java (11,14), IBM Java (7,8), Oracle Java (7,8)

IBM Cloudへのマイグレーションの際の診断もできるようで以下のオプションがあります。

--targetCloud=[containers|cfIBMCloud|thirdParty|vmIBMCloud]
指定したターゲット・クラウド・ランタイム環境へのマイグレーションに関する規則を
組み込みます。 デフォルトはありません。 ソースまたはターゲットのアプリケーション・サーバーを
指定せずにこのオプションを指定した場合、クラウド規則のみが組み込まれます。
以下のオプションが有効です。
* containers - コンテナー (IBM Cloud Pak for Applications、Kubernetes)
* cfIBMCloud - IBM Cloud ランタイム (CF PaaS)
* thirdParty - サード・パーティー PaaS (CF)
* vmIBMCloud - 仮想マシン (IBM Cloud)
`

JDKのインストール

 こちらからJDKをダウンロード、インストールしました。
https://www.oracle.com/java/technologies/javase-downloads.html
以下のコマンドが動けばいいです。

> java --version

出力例
image.png

Migration Toolkitのダウンロード、インストール

こちらのページからダウンロードをクリックしてダウンロードしてください。
https://developer.ibm.com/wasdev/downloads/#asset/tools-Migration_Toolkit_for_Application_Binaries
 binaryAppScannerInstaller.jarというファイルがダウンロードされます。PCの任意の場所に保存しました。
次に保存したフォルダーに移動して次のコマンドでインストール

> java -jar binaryAppScannerInstaller.jar

インストール後にwamtというフォルダーが作成されます。そこで以下のようにコマンドをいれて動作確認してください。

> cd wamt
java -jar binaryAppScanner.jar --help --all

以下のように使用方法が出てきます。
image.png

war/earファイルの準備

ここらへんを参考にwarファイルを作成しました。
https://techacademy.jp/magazine/23660#:~:text=WAR%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90%E6%96%B9%E6%B3%95,-WAR%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AF&text=%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%A7,%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%8C%E4%BD%9C%E6%88%90%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82

診断の実行

準備したwarファイルに対して以下のコマンドを入れればしばらくすると診断が始まります。そのあとブラウザーが起動して診断結果が表示されます。ここではHelloServelet.warを診断

> java -jar binaryAppScanner.jar HelloServlet.war

以下のように表示されました。
image.png

上記アプリは名前の通りHelloを出すだけのものなので問題がないのは当然ですが、ぜひいろいろなアプリで試してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaサーブレット 14 - 提出課題 社員情報管理ツール - 05

7. Class#forName再考

7.1. Class#foName処理

ロジックの生成にClass.forName()を使うべきか。このメソッドは何をしているのか。

クラスを取得する。ここでいうクラスとは、Javaコンパイラーが生成するclass拡張子付きのクラスファイルがJVMのメモリに読み込まれ、利用可能な状態に構成されたもの。まず、クラスは、クラスローダーによってメモリに読み込まれる。次に、利用可能な状態にするために、クラスの静的な(static)メンバーの初期化、静的初期化子の実行を一度だけ行う。 以下の例はClass#forNameを使用してorg.h2.Driverクラスをメモリーにロードする。

Class.forName("org.h2.Driver");

org.h2.Driverのソースコードを一部引用する。このクラスがメモリーにロードされると、staticで修飾された静的変数の初期化と、staticブロック(静的初期化子)が一度だけ実行される。その後、このクラスのインスタンスをnewしたり、静的メソッドを呼べるようになる。クラスがメモリに読み込まれた後はClass#forNameを実行しても意味がない。理由はすでにクラスがメモリに存在するから。ところが、Class#forNameを何度も実行すると、最悪の場合はスレッドの競合が発生して処理が遅延することがあることが知られている。

クラスのロードはクラスローダーが行う。クラスローダーの構成には階層がある。クラスローダーがクラスを発見できないときは上位のクラスローダーにクラスのロードを委譲する。

package org.h2;

public class Driver implements java.sql.Driver, JdbcDriverBackwardsCompat {

  private static final Driver INSTANCE = new Driver();
  private static final String DEFAULT_URL = "jdbc:default:connection";
  private static final ThreadLocal<Connection> DEFAULT_CONNECTION = new ThreadLocal<>();

  private static boolean registered;

  static {
    load();
  }

7.2. Class#foNameを利用する意味

利点として考えられるもの。

  • クラス名(String)を指定してクラスをロードできるのでクラスがなくてもコンパイルできる
  • 言い換えると、開発時点で存在しないクラスを実行時に名前でロードできる
    • これが働くためには、あらかじめインターフェイスや抽象クラスを用意しておき
    • 実行時にインターフェイスや抽象クラスを継承した具象クラスをクラス名でロードする
  • 用途はフレームワーク作成

欠点として考えられるもの。

  • 業務ロジックで利用する問題点
    • アプリケーションの初期化で一度だけ実行するべき機能、繰り返し実行するものではない
    • 最悪の場合は、性能低下、セキュリティの脆弱化を招く恐れがある

Class#foNameは、Webアプリケーションの初期化でフレームワークやDIコンテナが一度だけ実行すべき。通常処理で繰り返し使うべきではない。特にクラス名がインターネットに晒されるような実装は避けること。

7.3. ファクトリ機能

本来の要件は、利用者クラスにLogicインターフェイスだけを見せて、ポリモーフィズムを働かせることにあった。ファクトリ機能に、Logicを実装する具象クラスのインスタンスの生成を分離する。ファクトリ機能としてClass.forName()を使用したわけである。ファクトリ機能は、マーチン・ファウラー著、『新装版 リファクタリング』オーム社にswitch文の除去例として紹介されている。

JSPは、HTTPパラメータのactionに処理内容を表す文字を格納してサーブレットに送る。ファクトリメソッドを以下のように引数付きで定義する。引数のactionの内容に応じてLogicクラスを生成する。クラスが追加されてもファクトリメソッドのインスタンス生成部分を変更すれば、利用するクラスに影響を及ぼさない。

public static Logic getLogic(HttpServletRequest request) throws ServletException, IOException {

String action = request.getParameter("action");
  if (action == null) {
    return null;
  }
  switch(action) {
  case "編集":
    return new EditEmployeeLogic();
    break
  case "削除":
    return new DeleteEmployeeLogic();
    break
  defalut:
  }
  return null;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java GradleからのDocker利用

この記事はこのシリーズの第一弾です。

今回に限らず巨人の肩に乗ってパッケージを便利にしてきますか。
シリーズ冒頭からパッケージをさっくり作ったところでのスタートです。
なお、繰り返しですがソースコードあります。
適宜ご覧ください(READMEの口調が荒いですがお許しくだされ)。

導入プラグイン

今回利用するのは、米Palantir Technologies(データ分析業)の開発している、
docker pluginたちです。
「たち」というのは、docker, docker-run, docker-composeそれぞれに独立のプラグインがあるからですね。
https://github.com/palantir/gradle-docker
composeは今回はいいかなぁ...
ひとまずはgradleからcontainer起動までこぎつける、ここまでを扱います。

各手順

1. Dockerfile作成

まぁこれなければ動きませんよね...
今回作ったのはこんな感じです。

Dockerfile
FROM amazonlinux:latest

RUN yum update -y &&\
    yum upgrade -y &&\
    yum install java-11-amazon-corretto-headless -y

COPY ./build/libs/gradle-docker.jar /tmp/app.jar
ENTRYPOINT ["java", "-jar", "/tmp/app.jar"]

私はクラウド環境としてAmazon ECS / Fargate / EKS想定なのでamazonlinuxです(思考放棄)。
イメージサイズが重要であればubuntuやalpineでというのもありよりのありです。
alpineにcorretto突っ込むという荒業もできないことはないです...
Javaは11、LTSが無料で使えるのはありがたいですね。
ともかく、まずCOPYとENTRYPOINTはコメントアウトして、ちゃんと動くかまで確認します。

2. docker起動をビルドスクリプトに記述

ここが難所ですね。
まずプラグインを記入します。

build.gradle
plugins {
  id 'java'
  id 'application'
  id 'idea'
  id 'eclipse'
  id 'com.palantir.docker' version '0.25.0'
  id 'com.palantir.docker-run' version '0.25.0'
}

それから、プラグインが持っているtask:dockerに対し、いろいろオプションを加えていくわけです。
copySpecの下りはわかりにくいですので後述します。

build.gradle
docker {
  // name: これはbuildされたコンテナイメージの名前になります。
  // コロンで区切った右側はタグとして認識されます。
  name "${project.name}:${project.version}"
  // いわずもがなDockerfileです。
  // パスは.\つけても素でも動きます。
  dockerfile file('Dockerfile')
  // Gradleのコピースペックを使って、ビルドした資材をpluginの利用先に持っていきます。
  copySpec.from('build/libs').into('build/libs')
  // docker buildのキャッシュ利用off
  noCache true
  // 依存関係定義:gradle dockerしたら、その前段でgradle buildが走ります
  dependsOn tasks.build
}

これで、基本command lineで"gradle docker"としたら、走るはずです。
※DockerfileのCOPY/ENTRYPOINTはコメントアウトから戻してください。

まぁせっかくなので、ええいままよとcontainer起動まで行きますか。
プラグインはもう入っているのでタスクのオプションを追加するだけです。
コンテナ起動オプションは、特に人それぞれ異なるので、
ドキュメントをきっちり読んでくださいね。

build.gradle
dockerRun {
  //コンテナ名です。
  name project.name
  // 起動するイメージ(dockerタスクで作ったやーつ)
  image "${project.name}:${project.version}"
  // コンテナをデーモン起動?:docker run "-d" <- こいつ
  daemonize false  // 否、我顕現す
  // 君はマグロか?(stopしたらauto terminate)
  clean true // いかにも、マグロだ。
}

と、これで以下のコマンドをたたけばひとまずものになるはずです。

$ ./gradlew docker
$ ./gradlew dockerRun
Hello World.

ただ、もう少しやるべきことはあります。

3. fatJarをつくる

現在はライブラリがないのでちゃんと動きますが、
フレームワーク・ライブラリを突っ込むと、classNotFoundで落ちます。
かくなる上は、buildするjarファイルにライブラリjarを突っ込むしかない(わけではないが楽)。
これもpluginを使えば楽です。
ShadowJar、今回はこれを使いましょう。
まずプラグイン追加して、

build.gradle
plugins {
  id 'java'
  id 'application'
  id 'idea'
  id 'eclipse'
  id 'com.palantir.docker' version '0.25.0'
  id 'com.palantir.docker-run' version '0.25.0'
  id 'com.github.johnrengelman.shadow' version '6.1.0' // <-こいつ
}

それからタスクのオプションを追加します。
中にfinalizedByとあります。
これはdockerタスクを追加したときには伝えておりませんでしたが、
ちゃんとこのタスクが終わってから次のタスクに移るための明示です。
つまりこの場合、shadowJarタスクでjarを作り終わってからdockerタスクをさせたいということです。
後続するタスクのdependsOnをつけることで、確かにshadowJar->dockerで実行されます。
ただ、dockerタスクが動く前にshadowJarが完了することは保証されません。
正直ちょっと詰まりました...タスク依存ムズイ...

build.gradle
shadowJar {
  // jarタスクのを参照してくれないので、ここでもarchiveファイルは指定
  archiveFileName = "${project.name}.jar"
  // 依存関係
  finalizedBy tasks.docker
}

そして、前述のgradle docker/dockerRunコマンドでちゃんとイメージ作成・起動までこぎつけられたことでしょう。

内部での動き

dockerプラグイン君ですが、keyはイメージに突っ込むファイルです。
プラグインは/build/docker配下に資材(Dockerfile含)をため込み、
それをimageビルドに利用しています。
なので、dockerタスクのオプションでそれをしなければなりません。
プラグインはGradleの"copySpec"というAPIを使っています。
それで、
from /build/libs/*
to /build/docker/build/libs/*
こんな形でファイルをコピーさせているということですね。
それで、build/docker配下に同じくコピーされたDockerfileは、
その置き場所から./build/libs/your.jarを見つけ、COPYでイメージに突っ込んでいます。
ちなみに、task dockerのオプション file, copySpecを組み合わせて同様のことができます。

build.gradle
docker {
  name "${project.name}:${project.version}"
  dockerfile file('Dockerfile')
  files shadowJar.archiveFile.get()
  copySpec.into('build/libs')
  noCache true
  dependsOn tasks.shadowJar
}

filesオプションを事前に突っ込むことで、それもcopySpecに載せられます。
これを利用し、jarに梱包しない環境ファイルが別にある場合もイメージに突っ込めます。

開発に利用するなら

釈迦に説法かもしれませんが、
Dockerfileは書き換えての開発をお勧めします。
毎回gradle dockerでイメージビルドするのに、jdk落としてインスコしては萎えますので。
いっそjava環境まで作ったイメージをつくり、
以降はその部分をコメントアウトしてFROMを作ったイメージに向けるほうが圧倒的コスパです。

だそく

しかしこのプラグイン群すさまじいですね、やれることが多くて。
それこそimage pushやcompose利用など、いろいろできます。
こうしたものがあり、利用できるからエンジニアやれているのだと常々感じます。

ところで、話変わりますが「巨人の肩に乗る」って比喩でなくほんとに巨人なんですね。
SFの話です。
テューリアンは優しい。


それから、ちょっと面倒なので、docker rmiもgradleタスクで作ろうと思います。
プラグインに期待するのも1つですが、まぁできないことはないだろうと。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaジャーニー:快適なパッケージを求めて

ほぼ動的型付け言語しか使用しないチームに転職し、
┌( ^o^┌ )┐ <- これのごとく型付けェ型付けェと禁断症状に...
欲求不満に任せ、いつのまにか理想のGradleパッケージのジャーニーへと旅立っていました。
せっかくなので情報整理がてらやりたいこと・過程を紹介します。
あと、Githubにpublicのrepositoryを作っているので、
それをご覧いただくもよし、そのまま流用するもよし(そんな価値があればよいですが...)。
Github Link: gradle-docker
※本記事はJava/Gradle/Dockerなど基礎知識はすでにある前提でお読みください。

やりたいこと

  1. Dockerにすんなり疎通できる
  2. テストのフローはきっちり(Junit*Matcher - Jacocoでカバレッジ)(YET)
  3. Logも今風(slf4j*logback - Jsonized Log)(YET)
  4. 書き方も統制しよう(formatter, linter)(YET)

ここまでやれば、まぁ禁断症状は治まるかと...
あと、これをベースにAWSのコンソール操作を自動化するツールも作るつもりなので...

プロローグ

生まれたての小鹿パッケージ、作るだけ、作りますか。
gradle initの時の選択肢は、まぁオーソドックスですね。
javaですし、DSLはgroovyですし、テストはJUnitですし?。

$ cd '任意のディレクトリ'
$ mkdir 'project name'
$ cd 'project name'
$ gradle init --type java-application
$ gradle wrapper

これで、よくある、ローカルにjavaさえ入っていれば
- maven,gradleなくともできますよ、
- gradleは固定ですよ
の環境が出来上がりですね。
あとは最低限のプロジェクト・パッケージ情報をシコシコ仕込みますか。

gradle.build
plugins {
  id 'java'
  id 'application'
  id 'idea'
  id 'eclipse'
}

repositories {
  mavenCentral()
}
// パッケージのバージョン指定
project.version = '0.1.1'
// javaランタイムは11(15はそのうち...)
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11

dependencies {
  testImplementation 'junit:junit:4.13'
}

// Runするとき、Appのmain関数を起点に指定
application {
  mainClassName = 'info.akotadakura.App'
}

// UTF-8強要します。
tasks.withType(JavaCompile).all {
  options.encoding = "UTF-8"
}

// java -jarするとき、Appのmain関数を起点に指定
// あとjarファイル名にバージョンつくの嫌だ!
jar {
  archiveFileName = "${project.name}.jar"
  manifest {
    attributes 'Main-Class' : 'info.akotadakura.App'
  }
}

実に初々しい...
と、ここにいろいろ付け加えていくことになります。
まずはコンテナ対応、Docker Nativeな時代ですからね。

P.S.
初投稿なので、まさかりはお手柔らかに.....

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gradleジャーニー:快適なパッケージを求めて

ほぼ動的型付け言語しか使用しないチームに転職し、
┌( ^o^┌ )┐ <- これのごとく型付けェ型付けェと禁断症状に...
欲求不満に任せ、いつのまにか理想のGradleパッケージのジャーニーへと旅立っていました。
せっかくなので情報整理がてらやりたいこと・過程を紹介します。
あと、Githubにpublicのrepositoryを作っているので、
それをご覧いただくもよし、そのまま流用するもよし(そんな価値があればよいですが...)。
Github Link: gradle-docker
※本記事はJava/Gradle/Dockerなど基礎知識はすでにある前提でお読みください。

やりたいこと

  1. Dockerにすんなり疎通できる
  2. テストのフローはきっちり(Junit*Matcher - Jacocoでカバレッジ)(YET)
  3. Logも今風(slf4j*logback - Jsonized Log)(YET)
  4. 書き方も統制しよう(formatter, linter)(YET)

ここまでやれば、まぁ禁断症状は治まるかと...
あと、これをベースにAWSのコンソール操作を自動化するツールも作るつもりなので...

プロローグ

生まれたての小鹿パッケージ、作るだけ、作りますか。
gradle initの時の選択肢は、まぁオーソドックスですね。
javaですし、DSLはgroovyですし、テストはJUnitですし?。

$ cd '任意のディレクトリ'
$ mkdir 'project name'
$ cd 'project name'
$ gradle init --type java-application
$ gradle wrapper

これで、よくある、ローカルにjavaさえ入っていれば
- maven,gradleなくともできますよ、
- gradleは固定ですよ
の環境が出来上がりですね。
あとは最低限のプロジェクト・パッケージ情報をシコシコ仕込みますか。

gradle.build
plugins {
  id 'java'
  id 'application'
  id 'idea'
  id 'eclipse'
}

repositories {
  mavenCentral()
}
// パッケージのバージョン指定
project.version = '0.1.1'
// javaランタイムは11(15はそのうち...)
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11

dependencies {
  testImplementation 'junit:junit:4.13'
}

// Runするとき、Appのmain関数を起点に指定
application {
  mainClassName = 'info.akotadakura.App'
}

// UTF-8強要します。
tasks.withType(JavaCompile).all {
  options.encoding = "UTF-8"
}

// java -jarするとき、Appのmain関数を起点に指定
// あとjarファイル名にバージョンつくの嫌だ!
jar {
  archiveFileName = "${project.name}.jar"
  manifest {
    attributes 'Main-Class' : 'info.akotadakura.App'
  }
}

実に初々しい...
と、ここにいろいろ付け加えていくことになります。
まずはコンテナ対応、Docker Nativeな時代ですからね。

P.S.
初投稿なので、まさかりはお手柔らかに.....

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む