2018年10月16日火曜日

Eclipse のヘッドレスアプリケーションを作る

Eclipse のヘッドレスアプリケーションプラグインを作り、 コマンドラインからプラグインの処理を呼び出す。

本当は Java11 でやりたかったのだけれど、 「エクスポートした jar に class ファイルが格納されない問題」が解決できなかった。

なので、Java8 で Eclipse 4.9 を動かしている。

環境

  • OS: Windows 10 Pro
  • Java: java version "1.8.0_151"
  • Eclipse: Version: 2018-09 (4.9.0)
    • for Commiters

Eclipse の準備

Eclipse のダウンロードと展開

Enabling Open Innovation & Collaboration | The Eclipse Foundation からダウンロードする。

  1. Download -> Tool Platforms セクションの Download Packages
  2. Eclipse IDE for Eclipse CommittersWindows 64-bit
  3. Download ボタンからダウンロード
  4. ダウンロードした zip ファイルを適当な場所に展開

Eclipse を起動する Java の指定

僕の作業用 Windows は、デフォルトの Java を 11 にしている。 最初に説明したとおり、 11 だとうまくエクスポートができない。 そのため、Java 8 で Eclipse が動くように修正する。

eclipse.ini をテキストエディタで開き、 -vmargs の直前に、次の 2 行を追加する。 (\PATH\TO\JDK1.8.0\bin\javaw.exe は自分の環境に合わせて書き換える)

-vm
\PATH\TO\JDK1.8.0\bin\javaw.exe

プラグイン作成

eclipse を起動し、プラグインを開発していく。

プラグインプロジェクトの作成

  1. Plug-in Development パースペクティブを開く
  2. File -> New -> Plug-in Project
  3. 基本情報設定 -> Next
    • Project name: HeadlessApplication
  4. コンテント情報入力 -> Next
    • Options のチェックを全部外す
    • Rich Client Application 内のラジオボタンで Yes を選択
  5. テンプレートの選択 -> Finish
    • Headless Hello RCP を選択

この状態で、「呼び出すと Hello RCP World! と標準出力されるヘッドレスアプリケーション」が生成される。

プラグインのエクスポート

  1. プロジェクト右クリック -> Export
  2. Deployable plug-ins and fragments を選択して Next
  3. エクスポート設定 -> Finish
    • HeadlessApplication にチェックを入れる
    • Destination タブの Directory に、出力先ディレクトリを入力

Directory に指定したディレクトリ内に、 plugins ディレクトリが作成され、 その中にプラグインの jar ファイルが格納される。

動作確認

プラグインのインストール

「プラグインのエクスポート」で作成された jar ファイルを、 eclipse の dropins ディレクトリにコピーする。それだけ。 (本当はいろいろ作法があるらしい?未調査。)

動作確認

標準出力を確認したいので、 java.exe を使って実行する。

eclipsec.exe -nosplash -application HeadlessApplication.application -vm \PATH\TO\JDK1.8.0\bin\java.exe

下記出力が得られれば成功。

org.eclipse.m2e.logback.configuration: The org.eclipse.m2e.logback.configuration bundle was activated before the state location was initialized.  Will retry after the state location is initialized.
Hello RCP World!

あとはこれにお好きな処理を追加していくだけ。

環境に応じた Preference の値を流し込むとか、 Eclipse 操作のオートメーションとかできるといいなぁ...。

以上。

参考資料

2018年10月14日日曜日

Java 11 + Spring Boot 2 + Spring Data JPA で PostgreSQL にアクセスする

こんな感じのことをします。(察して...)

DB アクセスのエッセンスに注目したかったので、 CLI アプリとして作ります。

環境

  • OS: Windows 10 Pro
  • Java: openjdk version "11" 2018-09-25
  • Docker: Docker version 18.06.1-ce, build e68fc7a

依存定義

次の jar を依存関係として定義した。

Java9(11?) からの変更の影響により、 jaxb-apijavassist を明示的に追加する必要が出てきたらしい。

dependencies {
    compile("org.springframework.boot:spring-boot-starter:2.0.5.RELEASE")
    compile('org.springframework.boot:spring-boot-starter-data-jpa:2.0.5.RELEASE')
    runtime('org.postgresql:postgresql')

    // https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api
    compile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'

    // https://mvnrepository.com/artifact/org.javassist/javassist
    compile group: 'org.javassist', name: 'javassist', version: '3.23.1-GA'
}

PostgreSQL 環境の構築

構成

  • サーバー: PostgreSQL
  • DB: postgres

こんな感じで構築する。

               List of relations
 Schema |      Name      |   Type   |  Owner
--------+----------------+----------+----------
 public | message        | table    | postgres
 public | message_id_seq | sequence | postgres
(2 rows)
                                    Table "public.message"
 Column  |          Type          | Collation | Nullable |               Defaul
t
---------+------------------------+-----------+----------+-------------------------------------
 id      | integer                |           | not null | nextval('message_id_seq'::regclass)
 message | character varying(255) |           | not null |
Indexes:
    "message_pkey" PRIMARY KEY, btree (id)

環境を作る

Docker オフィシャルの PostgreSQL イメージを使って作成する。

Docker オフィシャルの PostgreSQL イメージは、コンテナ起動時に指定した sql を実行する機能があるので、その仕組みを使ってデータを入れる。

初期データ作成 sql

$(pwd)/schema/schema.sql を作成。

DROP TABLE IF EXISTS message;
DROP SEQUENCE IF EXISTS message_id_seq;

CREATE SEQUENCE message_id_seq START WITH 1 INCREMENT BY 1;

CREATE TABLE message (
  id INTEGER DEFAULT nextval('message_id_seq') PRIMARY KEY,
  message VARCHAR(255) NOT NULL
);

INSERT INTO message(message)
    VALUES
        ('テスト1'),
        ('テスト2');

コンテナ起動 + データ作成

次のコマンドで、 PostgreSQL サーバー起動と、前述の初期データの作成が完了する。

docker run -it --rm --name postgres -v "$(pwd)/schema:/docker-entrypoint-initdb.d" -p 5432:5432 postgres

プログラミング

エンティティの作成

まず、 DB から取得するレコードを表す Bean を作成する。

  • 今回は、 idmessage を取得したいので、それらをフィールドに持つ Bean を作る
  • アノテーション @Entity を付ける
  • プライマリキーに @Id アノテーションを付ける
  • id はシーケンスによる自動採番なので、それ関係のアノテーションを付ける
    • @SequenceGenerator
    • @GeneratedValue
package gs.postgres;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;

/**
 * Message
 */
@Entity
@SequenceGenerator(name = "message_id_seq", sequenceName = "message_id_seq", allocationSize = 1, initialValue = 1)
public class Message {
    @Id
    @GeneratedValue(generator="message_id_seq")
    private Long id;

    private String message;

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return String.format("id:%d, message=%s", this.id, this.message);
    }
}

リポジトリインターフェースを作成

DB への操作を抽象化してくれるやつを作る。

JpaRepository を継承したインターフェース定義だけ定義すれば、 Spring が良しなに実装を作ってくれるらしい。

継承する JpaRepository は、 JpaRepository<エンティティの型, エンティティのプライマリキーの型> とする。

package gs.postgres;

import org.springframework.data.jpa.repository.JpaRepository;

/**
 * MessageRepository
 */
public interface MessageRepository extends JpaRepository<Message, Long> {
}

DB にアクセスするプログラムを作る

情報取得と挿入、標準出力するプログラムを作成する。

  • repo フィールドに @Autowired アノテーションを付けているため、 Spring によって自動で MessageRepository のインスタンス化と代入が行われる。
    • MessageRepository は、SpringApplication.run(App.class, args) をした時点で、よしなに実装が作られるみたい
  • repoJpaRepository (Spring Data JPA 2.1.0.RELEASE API) のインターフェースが実装されているので、これを使って DB 操作ができる
package gs.postgres;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.domain.Sort;

@SpringBootApplication
public class App implements CommandLineRunner {

    // `SpringApplication.run` で実行した時点で、
    // よしなに `MessageRepository` クラスが注入されるらしい。
    @Autowired
    private MessageRepository repo;

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    /**
     * App 主処理
     *
     * 1. メッセージを全件取得して標準出力
     * 2. メッセージインサート
     * 3. メッセージを全件取得して標準出力
     *
     */
    @Override
    public void run(String... args) {

        // DB(postgres/message) から全件取得する
        List<Message> messages1 = repo.findAll(new Sort(Sort.Direction.ASC, "id"));

        // 取得した全件標準出力
        for (Message message : messages1) {
            System.out.println(message);
        }

        // 新規メッセージのインサート
        // ID はシーケンスからの自動採番なので null のままで良い
        Message insertMessage = new Message();
        insertMessage.setMessage("インサートメッセージ");
        repo.save(insertMessage);

        System.out.println("Message added.");

        // もう一度全件取得する
        List<Message> messages2 = repo.findAll(new Sort(Sort.Direction.ASC, "id"));

        // 取得した全件標準出力
        for (Message message : messages2) {
            System.out.println(message);
        }
    }
}

アプリの設定

src/main/resourcesapplication.propertieshibernate.properties を作成する。

application.properties

PostgreSQL に接続するための情報を記述。

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=

hibernate.properties

hibernate.properties はこのエラー対策

hibernate.jdbc.lob.non_contextual_creation = true

動作確認

ここまで作ったらあとは gradle run するだけ。

gradle run

以下のような感じで取得とインサートが出来ていることが確認できる。

> gradle run

> Task :run

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.5.RELEASE)

...(略)...

2018-10-14 14:36:00.877  INFO 25552 --- [           main] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
id:1, message=テスト1
id:2, message=テスト2
Message added.
id:1, message=テスト1
id:2, message=テスト2
id:3, message=インサートメッセージ

...(略)...

2018-10-14 14:36:01.047  INFO 25552 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

BUILD SUCCESSFUL in 5s
3 actionable tasks: 1 executed, 2 up-to-date

完成品はこちら

以上。

参考資料

2018年10月12日金曜日

Terraform で AWS の EC2 インスタンスを作って SSH 接続するまでをやってみる

Installing Terraform - Terraform by HashiCorp を見ながらやってみる。

環境・前提

Docker for Windows で hashicorp/terraform のイメージを使う。

  • Host OS: Windows 10 Pro
  • image: hashicorp/terraform:0.11.8
  • docker: Docker version 18.06.1-ce, build e68fc7a

必要パッケージのインストール

docker pull するだけ。

docker pull hashicorp/terraform:0.11.8

AWS の環境設定

AWS を Terraform で操作するための準備。 下記 3 つを作成する。

  • Terraform で AWS リソースをいじれるようにするための設定
    • グループ
    • ユーザー
  • EC2 インスタンスに SSH ログインできるようにするための下準備
    • EC2 のセキュリティグループ

ポリシーは、 AWS デフォルトポリシーのひとつを使用するので作成しなくてよい。

グループの作成

Terraform の aws_instance に必要な権限の記載が見つからなかったので、 AmazonEC2FullAccess を使用する。

  1. AWS マネジメントコンソール -> サービス -> IAM -> グループ
  2. 新しいグループの作成 -> グループ名入力 -> 次のステップ
    1. グループ名: Terraform
  3. AmazonEC2FullAccess のポリシーをアタッチ -> 次のステップ
  4. グループの作成

ユーザーの作成

  1. AWS マネジメントコンソール -> サービス -> IAM -> ユーザー
  2. ユーザーを追加 -> ユーザー名、アクセスの種類を入力 -> 次のステップ
    1. ユーザー名: mikoto2000-terraform
    2. アクセスの種類: プログラムによるアクセス
  3. アクセス許可の設定 -> 次のステップ
    1. ユーザーをグループに追加を選択
    2. グループ Terraform を追加する
  4. ユーザーの作成

作成直後に表示される、 アクセスキー IDシークレットアクセスキー をメモしておく。

EC2 セキュリティグループの作成

「どこからでも SSH アクセスしていいよ」というセキュリティグループを作る。

  1. AWS マネジメントコンソール -> サービス -> EC2 -> セキュリティグループ
  2. セキュリティグループの作成 -> セキュリティグループ名、説明、インバウンドルールを入力 -> 作成
    1. セキュリティグループ名: ssh
    2. 説明: Publish SSH port.
    3. インバウンド
      • タイプ: SSH
      • ソース: 任意の場所

Terraform 設定ファイルの作成

Getting Start の例 に、いくつか修正を加える。

  • 変数を使用
  • SSH のための設定を追加
    • key_name を追加
    • security_groups を追加

tf ファイル作成

getting_start.tf という名前で作成。

variable access_key {}
variable secret_key {}
variable key_name {}

provider "aws" {
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
  region     = "us-east-1"
}

resource "aws_instance" "example" {
  ami             = "ami-2757f631"
  key_name        = "${var.key_name}"
  instance_type   = "t2.micro"
  security_groups = ["ssh"]
}
  • Getting Start では指定していないが、 provider/version を指定することで、使用する provider のバージョンを固定できるようだ。(詳細はここを参照)
  • aws_instance/ami に、 Packer で作ったイメージを指定するなど、良い感じにイメージを選べばよいみたい。
  • SSH ログインできるように、下記を設定
    • /aws_instance/key_name : 前回作ったキーペアの名前を指定
    • /aws_instance/security_groups : 「EC2 セキュリティグループの作成」で作成したセキュリティグループ名を指定

変数ファイル作成

variables.tfvars という名前で作成。

AWS_ACCESS_KEY, AWS_SECRET_KEY はそれぞれ、ユーザーの作成 で確認した アクセスキー IDシークレットアクセスキー を設定する。

access_key = "AWS_ACCESS_KEY"
secret_key = "AWS_SECRET_KEY"
key_name   = "mikoto2000"

インスタンス作成

ざっくりいうと、下記 3 フェーズで、それぞれ terraform コマンドを叩いて実行していく。

  1. 初期化
  2. 計画の確認と適用
  3. 確認

以降は、 docker コンテナの中で作業していく。

docker run -v "\PATH\TO\WORK_DIR:/work" --workdir "/work" --entrypoint='' -it --rm hashicorp/terraform:0.11.8 /bin/sh

初期化

必要なプラグインのダウンロードとプロバイダの状態を記録するファイルの初期化処理を行う。

terraform init

計画の確認と適用

次のコマンドを実行すると、 terraform の実行計画が表示されるので、受け入れる場合には yes と打って Enter.

terraform apply -var-file=./variables.tfvars

動作確認

AWS に反映できていることを確認する。

マネジメントコンソールで確認

  1. AWS マネジメントコンソール -> サービス -> EC2 -> インスタンス
  2. 条件通りのインスタンスが作成されているかを確認する

SSH 接続

インスタンス情報の パブリック DNS (IPv4) のアドレスに、 Tera Term Pro で接続する。

  1. User name: ubuntu
  2. Use RSA/DSA/ECDSA/ED25519 key to log in を選択し、鍵に ~/.ssh/mikoto2000.pem を指定

はい成功。

後片付け

terraform destroy コマンドで、作成したリソースを削除できる。

terraform destroy -var-file=./variables.tfvars

ASW でインスタンスが削除されているのが確認出来たら OK.

参考資料

2018年10月7日日曜日

AWS を初めて触る状態から Packer を使ってインスタンスを作成してログインするまでをやってみる

Getting Start を見ながらやってみる。

環境・前提

Docker for Windows で hashicorp/packer のイメージを使う。

  • Host OS: Windows 10 Pro
  • image: hashicorp/packer:1.3.1
  • docker: Docker version 18.06.1-ce, build e68fc7a

AWS のアカウントは、作成だけした状態からスタート。

必要パッケージのインストール

docker pull するだけ。

docker pull hashicorp/packer:1.3.1

AWS の環境設定

Packer で AWS のリソースをいじるためには、 いろいろと権限が必要。

下記 3 つを作成する。

  • ポリシー
  • グループ
  • ユーザー

ポリシーの作成

必要な権限は Amazon AMI Builder のドキュメントに記載されている ので、この JSON をコピーしてポリシーを作る。

  1. AWS マネジメントコンソール -> サービス -> IAM -> ポリシー
  2. ポリシーの作成 -> JSON タブを開く -> 前述の JSON 定義を貼り付け -> Review policy
  3. 名前、説明を記述 -> Create policy
    1. ポリシー名: packer-basic

グループの作成

  1. AWS マネジメントコンソール -> サービス -> IAM -> グループ
  2. 新しいグループの作成 -> グループ名入力 -> 次のステップ
    1. グループ名: Packer
  3. packer-basic のポリシーをアタッチ -> 次のステップ
  4. グループの作成

ユーザーの作成

  1. AWS マネジメントコンソール -> サービス -> IAM -> ユーザー
  2. ユーザーを追加 -> ユーザー名、アクセスの種類を入力 -> 次のステップ
    1. ユーザー名: mikoto2000-packer
    2. アクセスの種類: プログラムによるアクセス
  3. アクセス許可の設定 -> 次のステップ
    1. ユーザーをグループに追加を選択
    2. グループ Packer を追加する
  4. ユーザーの作成

作成直後に表示される、 アクセスキー IDシークレットアクセスキー をメモしておく。

イメージ作成用テンプレートの作成

ベーステンプレートファイルの作成

とりあえず、 Getting Start の JSON をまるっとコピーし、 getting_start.json として保存。

{
  "variables": {
    "aws_access_key": "",
    "aws_secret_key": ""
  },
  "builders": [{
    "type": "amazon-ebs",
    "access_key": "{{user `aws_access_key`}}",
    "secret_key": "{{user `aws_secret_key`}}",
    "region": "us-east-1",
    "source_ami_filter": {
      "filters": {
      "virtualization-type": "hvm",
      "name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*",
      "root-device-type": "ebs"
      },
      "owners": ["099720109477"],
      "most_recent": true
    },
    "instance_type": "t2.micro",
    "ssh_username": "ubuntu",
    "ami_name": "packer-example {{timestamp}}"
  }]
}
  • 他のイメージを元に作成したい場合には、 builders/source_ami_filter/filters/ の各項目に、 AWS マネジメントコンソール -> AMI からイメージを検索したときに出てくる情報を入れればよいらしい。
  • 別リージョンに作成したい場合は、builders/regionここ のどれかにすればよいらしい。

変数ファイルの作成

キー類をハードコードするのが嫌だったのでやり方を探したら、 別 JSON に記載する方法 があったのでそれを採用することにした。

AWS_ACCESS_KEY, AWS_SECRET_KEY はそれぞれ、ユーザーの作成 で確認した アクセスキー IDシークレットアクセスキー を設定する。

{
  "aws_access_key": "AWS_ACCESS_KEY",
  "aws_secret_key": "AWS_SECRET_KEY"
}

作ったファイルの検証

hashicorp/Packe のイメージを起動。

docker run -v "\PATH\TO\WORK_DIR:/work" --workdir "/work" --entrypoint='' -it --rm hashicorp/packer:1.3.1 /bin/bash

コンテナ内で次のコマンドを実行する。

packer validate -var-file=./variables.json getting_start.json

成功なら Template validated successfully. と表示される。

イメージのビルド

引き続き、コンテナ内で次のコマンドを実行する。

packer build -var-file=./variables.json getting_start.json

最終的に下記のような成功ログが表示されるはず。 数分かかった。

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:
us-east-1: ami-0acbfa688dc2e0b4b
  1. Packer Builder というインスタンスを作る
  2. source_ami_filter の条件に引っかかったものを取得・展開
  3. 「2.」でとってきたものをイメージ化

みたいなことをしているっぽい。

完成したイメージは、 AWS マネジメントコンソール -> サービス -> EC2 -> AMI で確認できる。

動作確認

作成したイメージからインスタンス作成ができることを確認する。

インスタンス作成

  1. AWS マネジメントコンソール -> サービス -> EC2 -> インスタンス
  2. インスタンスの作成
    1. マイ AMI -> 今回作った AMI を選択
    2. t2.small を選択 -> 確認と作成
    3. 作成
    4. キーペアの作成
      1. 新しいキーペアの作成を選択
        • キーペア名: mikoto2000
      2. キーペアのダウンロード
        • ~/.ssh/mikoto2000.pem に保存
    5. インスタンスの作成

数分待つと、 AWS マネジメントコンソール -> サービス -> EC2 -> インスタンス に今作ったインスタンスが作成され、ステータスチェックがパスしていることが確認できる。

SSH 接続

インスタンス情報の パブリック DNS (IPv4) のアドレスに、 Tera Term Pro で接続する。

  1. User name: ubuntu
  2. Use RSA/DSA/ECDSA/ED25519 key to log in を選択し、鍵に ~/.ssh/mikoto2000.pem を指定

はい。

このあと、イメージのカスタマイズが必要であれば provisioners でがちゃがちゃする感じなのかな?

後片付け

これまでの手順で、インスタンス・AMI・スナップショットが作成されているので、それぞれ削除すること。 削除しないと課金されるはず。

それぞれは次の場所で削除可能。

  • インスタンス : AWS マネジメントコンソール -> サービス -> EC2 -> インスタンス
  • AMI : AWS マネジメントコンソール -> サービス -> EC2 -> AMI
  • スナップショット : AWS マネジメントコンソール -> サービス -> EC2 -> スナップショット

以上。

参考資料