20200806のJavaに関する記事は13件です。

【CodePipeline×Elastic Beanstalk】JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする、のエラーとその対応方法まとめ

JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする
で発生しうるエラーとその対応方法まとめです。

環境

  • OS:Windows10
  • IDE:Eclipse 2020-03
  • JDK:Amazon Correto 8
  • フレームワーク:Spring Boot
  • AWS
    • CodePipeline
      • CodeCommit
      • CodeBuild
      • CodeDeploy
    • Elastic Beantalk
      • Java SE(Java 8 バージョン 2.10.8)
        → ※2020年6月3日公開の最新版ではないです。ご了承を。
      • RDS:MySQL Community Edition(バージョン8.0.17)

エラーとその対策集

1. Elastic Beanstalk

(1) アクセス時に、502(Bad Gateway)エラーになる

設定内容を今一度確認しましょう。
【CodePipeline×ElasticBeanstalk】JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする その2
の「3. Elastic Beanstalk環境の作成」の「[2] 追加設定」を見直して、設定に漏れがないか確認してください。

2. RDS

(1) データベースにアクセスできない

アクセス許可の設定を今一度確認しましょう。
【CodePipeline×ElasticBeanstalk】JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする その2
の「4. データベースの設定」の「[1] RDSにおける接続設定」の(4)~(9)辺りを見直して、漏れがないか確認してください。

3. CodePipeline

(1) デプロイ時に「The action failed because either the artifact or the Amazon S3 bucket could not be found. ・・・」エラーが出る

image.png

入力アーティファクトをBuildArtifact → SourceArtifactへ変更すると解消します。
以下の手順で変更してください。

①パイプラインにて、「編集する」をクリック。
image.png

②「Deploy」欄にて、「ステージを編集する」をクリック。
image.png

③編集マーク(?)をクリック。
image.png

④「入力アーティファクト」を「SourceArtifact」に変更し(①)、「完了」をクリック(②)。
image.png

⑤「保存する」をクリック。
image.png

⑥「保存する」をクリック。
image.png

(2) 「Deployment failed. The provided role does not have sufficient permissions: Failed to deploy application. Service:AWSLogs・・・」が出る

CloudWatchLogsへのアクセス権がないために起こるエラーです。
CodePipeline用に作成されたロールに、「CloudWatchLogsFullAccess」ポリシーを付与することで、解決できます。

以下、手順です。

①マネジメントコンソールにて、「IAM」を探してクリック。
image.png

②「ロール」(①) > 対象のCodePipelineのロールを検索し(②)、ロール名をクリック(③)。
image.png

③「ポリシーをアタッチします」をクリック
image.png

④「CloudWatchLogsFullAccess」を検索して(①)、チェックを入れ(②)、「ポリシーのアタッチ」(③)をクリック。
image.png

終わりに

本編にも書きましたが、自分は、本編の1-[3]-(5)のgradlewの権限変更で3日、そして、5-[1]-(10)のデプロイの段階で3回エラーで躓いて何度も心が折れそうになりました。
エラーが発生しても、めげずに、むしろ、どうやって解決しよっかなーって感じで、楽しんでいきましょう!

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

Java で Mustache テンプレートエンジンの JMustache を使うサンプルコード

概要

  • Java で Mustache テンプレートエンジンの JMustache を使う
  • 今回の動作確認環境: Java 14 (AdoptOpenJDK 14.0.2+12) + JMustache 1.15 + Gradle 6.5.1 + macOS Catalina

サンプルコード

ソースコード一覧

├── build.gradle
└── src
    └── main
        ├── java
        │   ├── SampleApp.java
        │   └── SampleData.java
        └── resources
            └── my_template.html

build.gradle

plugins {
  id 'application'
}

repositories {
  mavenCentral()
}

dependencies {
  // https://mvnrepository.com/artifact/com.samskivert/jmustache
  implementation 'com.samskivert:jmustache:1.15'
}

application {
  mainClassName = 'SampleApp'
}

src/main/java/SampleApp.java

import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

public class SampleApp {

  public static void main(String[] args) throws IOException {
    new SampleApp().invoke();
  }

  public void invoke() throws IOException {

    // Mustache テンプレートファイルを読み込む
    String templateText = getResourceText("my_template.html");
    Template template = Mustache.compiler().compile(templateText);

    // オブジェクトを Mustache テンプレートに流し込む
    SampleData data = new SampleData();
    String out = template.execute(data);

    // 結果を出力する
    System.out.println(out);
  }

  // リソースのディレクトリからファイルを読み込む
  public String getResourceText(String path) throws IOException {
    try (InputStream is = getClass().getResourceAsStream(path)) {
      return new String(is.readAllBytes(), StandardCharsets.UTF_8);
    }
  }
}

src/main/java/SampleData.java

import java.util.List;
import java.util.Map;

public class SampleData {

  public String foo = "foo";

  public String getBar() {
    return "bar";
  }

  public String[] strings = {"S1", "S2", "S3"};

  public List list = List.of("L1", "L2", "L3");

  public Map map = Map.of("key1", "value1", "key2", "value2", "key3", "value3");
}

src/main/resources/my_template.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
foo: {{foo}}<br>
getBar(): {{bar}}<br>
<br>
strings:<br>
{{#strings}}
  value: {{.}}<br>
{{/strings}}
<br>
list:<br>
{{#list}}
  value: {{.}}<br>
{{/list}}
<br>
map:<br>
{{#map}}
  {{key1}}, {{key2}}, {{key3}}
{{/map}}
</body>
</html>

Gradle で実行

$ gradle run

> Task :run
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
foo: foo<br>
getBar(): bar<br>
<br>
strings:<br>
  value: S1<br>
  value: S2<br>
  value: S3<br>
<br>
list:<br>
  value: L1<br>
  value: L2<br>
  value: L3<br>
<br>
map:<br>
  value1, value2, value3
</body>
</html>

参考資料

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

【CodePipeline×ElasticBeanstalk】JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする その3

Javaアプリケーション(データベースから値を取得し、JSON形式で結果を返す)をCodePipelineでElasticBeanstalkにCI/CDするハンズオンです。
【CodePipeline×ElasticBeanstalk】JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする その2
の続きです。

環境

  • OS:Windows10
  • IDE:Eclipse 2020-03
  • JDK:Amazon Correto 8
  • フレームワーク:Spring Boot
  • AWS
    • CodePipeline
      • CodeCommit
      • CodeBuild
      • CodeDeploy
    • Elastic Beantalk
      • Java SE(Java 8 バージョン 2.10.8)
        → ※2020年6月3日公開の最新版ではないです。ご了承を。
      • RDS:MySQL Community Edition(バージョン8.0.17)

手順

5. パイプラインの作成

いよいよ最終段階。
CodePipelineを作成して、Elastic Beanstalk環境にデプロイします。

[1] CodePipelineの作成

(1)マネジメントコンソールにて、CodePipelineを探し、クリック。
image.png

(2)「パイプラインを作成する」をクリック。
image.png

(3)「パイプラインの設定を選択する」画面に遷移したら、「パイプライン名」を入力し(①)、「新しいサービスロール」を選択し(②:デフォルト)、「次に」をクリック(③)。
これにより、パイプライン作成時に「ロール名」に記載のロールが作成されます。
image.png

(4)次に、「ソースステージを追加する」画面です。
以下の通り入力・選択し、「次に」をクリック(⑤)

No 名称 設定内容
ソースプロバイダー AWS CodeCommit
リポジトリ名 手順2:Gitリポジトリの作成で作成したリポジトリ名
ブランチ名 master
検出オプションを変更する Amazon CloudWatch Events(推奨)

image.png

(5) 続いて、「ビルドステージを追加」画面です。
「プロバイダーを構築する」にて、「AWS CodeBuild」を(①)、「リージョン」は「アジアパシフィック(東京)」を選択し(②)、「プロジェクトを作成する」をクリック(③)。
image.png

(6)「ビルドを作成する」ウィンドウが立ち上がります。
以下の通り入力・設定し、「CodePipelineに進む」をクリック。

No 名称 設定内容
プロジェクト名 任意の名称(今回は「sample-eb-java-build」)
環境イメージ マネージド型イメージ
オペレーティングシステム AmazonLinux 2
ランタイム Standard
イメージ aws/codebuild/amazonlinux2-x86_64-standard:3.0
イメージのバージョン 最新のものを選択
環境タイプ Linux
サービスロール 新しいサービスロール
ロール名 自動で入力される(必要に応じて変更)
ビルド仕様 ビルドコマンドの挿入
ビルドコマンド ※の通り入力
入力モード エディタ(キャプチャは「エディタに切り替える」をクリックしてエディタでの編集モードになっている状態)
※ビルドコマンドの内容
version: 0.2

phases:
  install:
    runtime-versions:
      java: corretto8
  build:
    commands:
       - ./gradlew test

image.png

(6) 「ビルドステージを追加する」画面に戻ったら、「CodeBuildで<プロジェクト名>が正常に作成されました。」となっていることを確認し(①)、「次に」をクリック(②)。
image.png

(7) 「デプロイ - オプショナル」画面に遷移したら、以下の通り入力・選択し、「次に」(⑤)をクリック。

No 名称 設定内容
デプロイプロバイダー AWS Elastic Beanstalk
リージョン アジアパシフィック(②)
アプリケーション名 「3. Elastic Beanstalk環境の作成」で作成したアプリケーション
環境名 「3. Elastic Beanstalk環境の作成」で作成した環境

image.png

(8) 「レビュー」画面に遷移したら、内容を確認し、「パイプラインを作成する」をクリック。
image.png

(9) パイプラインが作成され、「2. Gitリポジトリの作成」にて、コミット・プッシュされたコードが順に、ビルド、デプロイされます。
image.png

(10) デプロイまで成功(緑色のチェックマークがつく)になったら、OKです。
image.png

今後は、CodeCommitのリポジトリのmasterブランチに変更が加わるたびに、このパイプラインが実行されます。
実環境であれば、子ブランチからプルリク → マージされた場合に実行される形になるでしょう。

もし、パイプライン実行時にエラーが発生した場合は、
【CodePipeline×Elastic Beanstalk】JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする、のエラーとその対応方法まとめ
を参考に解決してみてください。

(11) Elastic Beanstalkの確認もしましょう。
マネジメントコンソールにて、「サービス」(①) > 「Elastic Beanstalk」の順にクリック。
image.png

(12) ナビゲーションペインにて、「環境」をクリック。
image.png

(13) 以下のように、ヘルスが「OK」となっていれば成功です!(①)
image.png

[2] 動作確認

(1) 動作確認をしましょう。
ブラウザにて、以下の通り入力&Enterして、以下のような結果が返却されれば成功です!

実行URL
(2)の②に記載のドメイン/shop-information/1

image.png

6. 後片付け

最後に後片付けを行います。
このまま放置しておくと、Elastic Beanstalk環境の作成にて作成された、EC2インスタンス、RDSインスタンスの稼働分だけ、課金されてしまいますので、後片付け(削除)しておきましょう。

[1]Elastic Beanstalk

(1) マネジメントコンソールにて、「サービス」(①) > 「Elastic Beanstalk」の順にクリック。
image.png

(2) ナビゲーションペインにて、「環境」をクリック。
image.png

(3) 対象の環境のラジオボタンを選択し(①)、「アクション」 > 「環境の終了」をクリック。
image.png

(4) 「環境の削除の確認」画面に遷移するので、環境の名前を入力し(①)、「削除」をクリック(②)。
image.png

(5) 続いて、「アプリケーション」の削除。
Elastic Beanstalkのナビゲーションペインにて、「アプリケーション」をクリック。
image.png

(6) 対象のアプリケーションのラジオボタンを選択し(①)、「アクション」 > 「アプリケーションの削除」をクリック。
image.png

(7)「アプリケーションの削除の確認」のモーダルが立ち上がるので、対象のアプリケーション名を入力し(①)、「削除」をクリック(②)。
image.png

[2]CodePipeline

(1) 次に、CodePipeline。
少なくとも、コードに変更を行わなければ料金は発生しないのですが、逆を言えば、なんらかの拍子に変更が加わってしまうと料金が発生してしまうので、こちらも削除しておきましょう。

マネジメントコンソールにて、「サービス」(①) > 「Code Pipeline」の順にクリック。
image.png

(2) 対象のパイプラインのラジオボタンを選択し(①)、「パイプラインを削除する」をクリック(②)。
image.png

(3) 削除確認のモーダルが立ち上がるので、「delete」と入力し(①)、「削除」をクリック(②)
image.png

終わりに

長時間お疲れさまでした。

自分は、1-[3]-(5)のgradlewの権限変更で3日、そして、5-[1]-(10)のデプロイの段階で3回エラーで躓いて何度も心が折れそうになりました。
だけど、できた時の達成感はハンパない!!
少しでも多くの皆さんのお役に立てばと思い、記事にまとめました。

もし至らない点、気づいた点などあればコメントいただければ幸いです。

参考

5. パイプラインの作成

6. 後片付け

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

【CodePipeline×ElasticBeanstalk】JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする その2

Javaアプリケーション(データベースから値を取得し、JSON形式で結果を返す)をCodePipelineでElasticBeanstalkにCI/CDするハンズオンです。
【CodePipeline×ElasticBeanstalk】JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする その1
の続きです。

環境

  • OS:Windows10
  • IDE:Eclipse 2020-03
  • JDK:Amazon Correto 8
  • フレームワーク:Spring Boot
  • AWS
    • CodePipeline
      • CodeCommit
      • CodeBuild
      • CodeDeploy
    • Elastic Beantalk
      • Java SE(Java 8 バージョン 2.10.8)
        → ※2020年6月3日公開の最新版ではないです。ご了承を。
      • RDS:MySQL Community Edition(バージョン8.0.17)

手順

3. Elastic Beanstalk環境の作成

続いて、Elastic Beanstalk環境を作成します。
環境作成 → EC2起動してしまうと、課金の対象になってしまうので、できれば手順5のパイプラインの作成まで一気に駆け抜けるのがベスト!
手順1 ,2でちょっと疲れたな―って方は一度お休みして、気力が回復してから臨んでください。

では、いってみましょー!

[1] 環境、アプリケーションの新規作成

(1) AWSマネジメントコンソールにログインし、「サービス」(①) > 「Elastic Beanstalk」を探してクリック。
image.png

(2) ナビゲーションペインにて、「環境」をクリック。
image.png

(3) 「新しい環境の作成」をクリック。
image.png

(4) 「ウェブサーバー環境」を選択し(①)、「選択」をクリック(②)。
image.png

(5)「ウェブサーバー環境の作成」ページに遷移するので、以下の通り入力・設定し、「より多くのオプションの設定」をクリック(⑧)。

No 名称 設定内容
アプリケーション名 sample-eb-java
環境名 アプリケーション名を入力すると自動で入力される(※1)
プラットフォーム Java
プラットフォームのブランチ Java 8 running on 64bit Amazon Linux
プラットフォームのバージョン 2.10.9:Recommendedとなっているもの
アプリケーションコード 「コードのアップロード」を選択
ソースコード元 「ローカルファイル」を選択し、zipファイルをアップロード(※2)

※1:すでに同名の環境が存在する場合は、環境名の後ろに「-n」(nは数字)が入る。
※2:Elastic Beanstalk での Java の開始方法 | awsより、「java-se-jetty-gradle-v3.zip」を取得し、使用。

image.png

(6) オプションの設定画面に遷移します。全容は以下のキャプチャの通り。
image.png

今回は、

  • ①:ソフトウェア
  • ②:ネットワーク
  • ③:データベース

の3つに手を入れます。

(7)まず、①ソフトウェアから。
「ログのストリーミング」の「有効」にチェックを入れ、画面下方の「保存」をクリック。
こうしておくことで、ログが見られるようになるので、環境でのエラーの原因を調べる際に便利です。
image.png

(8) 続いて、②ネットワーク。
パブリックIPアドレス(①)、アベイラビリティーゾーン(②)の左隣のチェックボックスにチェックし、「保存」をクリック(③)。

image.png

(9) 最後にデータベース。
ユーザー名(①)、パスワード(②)を設定し、保存をクリック(③)。
image.png

(10)「より多くのオプションの設定」画面にて、「環境の作成」をクリック

(11)10分ほどして環境が作成されたら、「ヘルス」が「OK」となっていることを確認し(①)、環境名下のURL(②)をクリック
image.png

(12) 以下のようなページが表示されれば成功です!
image.png

[2] 追加設定

(1) 追加設定もしておきしょう。Elastic Beanstalkのナビゲーションペインの「設定」をクリック。
image.png

(2)「インスタンス」の右側の「編集」をクリック。
image.png

(3)「デフォルト」にチェックを入れ(①)、「適用」をクリック(②)。
【重要】忘れると、通信がうまくいかず、502(Bad Gateway)となりますので、必ず設定してください。
image.png

4. データベースの設定

続いて、3. Elastic Beanstalk環境の作成によってできた、データベース(RDS)の設定を行いましょう。

[1] RDSにおける接続設定

(1) マネジメントコンソールにて、RDSを探し、クリック。
image.png

(2) ナビゲーションペインにて、「データベース」をクリック。
image.png

(3) 作成されたデータベースインスタンスのラジオボタンにチェックを入れ(①)、「変更」をクリック(②)。
image.png

(4) データベースインスタンスの変更画面に遷移するので、「ネットワーク&セキュリティ」欄の「パブリックアクセシビリティ」を「はい」にし、画面下方の「次へ」をクリック。
image.png

(5)「変更の概要」画面に遷移したら、内容を確認し、「変更のスケジュール」の「すぐに適用」を選択し(①)、「DBインスタンスの変更」をクリック(②)。
image.png

(6)「データベース」画面に戻ったら、データベースインスタンス名の上のリンクをクリック。
image.png

(7) 「接続とセキュリティ」タブにて、「VPCセキュリティグループ」(リンク)をクリック。
image.png

(8)「セキュリティグループ」画面に遷移したら、「インバウンドルール」タブをクリックし(①)、「インバウンドルールを編集」をクリック(②)。
image.png

(9)「任意の場所」を選択し(①)、「ルールを保存」をクリック(②)。
【注意】この設定内容は全ての接続元からのアクセスを許可してしまうため、セキュリティ上よくありません。
今回は無線LANを使ってクライアントPCからの接続も行うため、この設定としています。
image.png

[2] テーブルの作成

(1) 続いて、接続確認も兼ねて、クライアントPCから、テーブルの作成を行いましょう。
コマンドプロンプトを起動し、以下のコマンドを実行、データベースインスタンスへ接続します。

実行コマンド
> mysql -h ホスト名-P ポート番号 -u ユーザー名 -p

ここで、ホスト名は以下のキャプチャの「エンドポイント」(①)、ポート番号はか「ポート」(②:デフォルトは3306)、ユーザー名は3. Elastic Beanstalk環境の作成で設定した「ユーザー名」になります(今回はadmin)。

image.png

コマンド実行後、パスワードを聞かれるので、Elastic Beanstalk環境の作成で設定した「パスワード」を入力し、Enter。

ログインできたら、以下のDDL、SQLを実行し、データベース、テーブルの作成、ならびに、データの挿入を行っておきましょう。

データベースとテーブルの作成
CREATE DATABASE `sample-db` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin;

USE `sample-db`;

CREATE TABLE `shop_informations` (
  `shop_id` mediumint(9) NOT NULL AUTO_INCREMENT COMMENT '店舗ID',
  `shop_name` varchar(100) DEFAULT NULL COMMENT '店舗名',
  `tel` varchar(15) DEFAULT NULL COMMENT '電話番号',
  `zip_code` varchar(10) DEFAULT NULL COMMENT '郵便番号',
  `address` varchar(100) DEFAULT NULL COMMENT '住所',
  `access` varchar(100) DEFAULT NULL COMMENT 'アクセス',
  `business_hour` varchar(100) DEFAULT NULL COMMENT '営業時間',
  `regular_holiday` varchar(100) DEFAULT NULL COMMENT '定休日',
  PRIMARY KEY (`shop_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='店舗情報';
begin;

INSERT INTO shop_informations VALUES(1, '春風カフェ', '03-XXXX-XXXX', '100-0005', '東京都千代田区丸の内・・・', '東京駅から徒歩5分', '平日 7:00~21:00/土日祝 10:00~21:00', '不定休');
INSERT INTO shop_informations VALUES(2, '夏海食堂', '050-XXXX-XXXX', '254-0034', '神奈川県宝町・・・', '平塚駅から徒歩10分', '平日 11:00~22:00/土日祝 11:00~20:00', '火曜日');
INSERT INTO shop_informations VALUES(3, '割譲秋山', '049-XXXX-XXXX', '350-0041', '埼玉県川越市六軒町・・・', '川越市駅から徒歩7分/本川越駅から徒歩5分', '昼 11:00~14:00/夜 18:00~23:30', '日曜・祝日');
INSERT INTO shop_informations VALUES(4, '冬空キッチン', '04-XXXX-XXXX', '350-0041', '千葉県我孫子市本町・・・', '我孫子駅から徒歩8分', '昼 11:30~14:00/夜 17:00~23:00', '木曜日');

commit;

※上記データは、架空のものになります。実在する店舗ではありません。

[3] Elastic Beanstalkにおける設定

(2) 続いて、Elastic Beanstalkにて、データベース接続用の環境変数を追加します。
マネジメントコンソールにて、Elastic Beanstalkを探してクリック。
image.png

(3) 「ソフトウェア」の右端の「編集」をクリック。
image.png

(4) 「環境のプロパティ」欄にて、以下のように設定し(①)、「適用」をクリック(②)。

名前
DB_HOST (10)で入力したホスト名
DB_PORT (10)で入力したポート番号
DB_NAME (10)で作成したデータベース名
DB_USER (10)で入力したユーザー名
DB_PASSWORD (10)で入力したパスワード
SERVER_PORT 5000

image.png

(5) 「1. Java(Spring Boot)アプリケーションの作成」で作成したapplication.ymlの内容を以下のように書き換えます。

application.yml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${DB_HOST:(10)で入力したホスト名}:${DB_PORT:(10)で入力したポート番号}/${DB_NAME:(10)で作成したデータベース名}?serverTimezone=JST
    username: ${DB_USERNAME:(10)で入力したユーザー名}
    password: ${DB_PASSWORD:(10)で入力したパスワード}
  jpa:
    database: MYSQL
    hibernate.ddl-auto: none
server:
  port: ${SERVER_PORT:5000}

こうすることで、クライアントPCから、今回作成したデータベースに接続することができるようになります。

(6) Eclipseにて、作成したプロジェクト上で右クリックし(①)、「実行」(②) > 「Spring Bootアプリケーション」(③)をクリックして、ローカルサーバーを起動しましょう。
image.png

(7) ブラウザを立ち上げて、以下のURLを叩き、結果が取得できることを確認します。

実行URL
http://localhost:5000/shop-information/1

(8) application.ymlの変更内容をGitのリモートリポジトリ(CodeCommit)にも反映させます。
コマンドプロンプトを起動して、プロジェクトのルートディレクトリまで移動し、以下の順でコマンドを実行。

> git add application.yml
> git commit -m "fix application.yml"

続編について

続きは、

にて。
5. パイプラインの作成の接続設定と6. 後片付けを行います。

参考

手順

3. Elastic Beanstalk環境の作成

4. データベースの接続設定

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

【CodePipeline×ElasticBeanstalk】JavaアプリケーションをCodePipelineでElasticBeanstalkにCI/CDする その1

Javaアプリケーション(データベースから値を取得し、JSON形式で結果を返す)をCodePipelineでElasticBeanstalkにCI/CDするハンズオンです。

長いので、

にの3記事に内容を分割しています。
作業時間は事前準備を除いて、トータル3時間前後を想定。各記事1時間程度が目安。

環境

  • OS:Windows10
  • IDE:Eclipse 2020-03
  • JDK:Amazon Correto 8
  • フレームワーク:Spring Boot
  • AWS
    • CodePipeline
      • CodeCommit
      • CodeBuild
      • CodeDeploy
    • Elastic Beantalk
      • Java SE(Java 8 バージョン 2.10.8)
        → ※2020年6月3日公開の最新版ではないです。ご了承を。
      • RDS:MySQL Community Edition(バージョン8.0.17)

イメージ図

本手順でできる内容のざっくりとしたイメージ図です。
ローカルで編集したコードをコミット → CodeCommitにプッシュすると、CodePipelineにより、アプリケーション実行環境であるElastic Beanstalkまで自動でビルド、デプロイを行ってくれるCI/CDの仕組みを構築します。
image.png

事前準備

①Eclipseが未インストールの場合は、
Pleiades All in One Eclipse ダウンロード(リリース 2020-06) | MergeDoc Project
より最新版(2020年8月3日時点)をダウンロードしてください。STS と Lombok が設定済みのため、Spring Bootによる開発にすぐ着手できます。

②JDKはAmazon Correto 8を想定しています。
Amazon Corretto 8 のダウンロード | aws
より、ダウンロード→インストールし、Eclipseへの設定を済ませておいてください。

③CodeCommitが使えるよう、
AWSのCodeCommit使い方。アクセスキー作るところからなど。| Qiita
を参考に、事前準備を行っておいてください。

手順

1. Java(Spring Boot)アプリケーションの作成

まず、EclipseでJava(Spring Boot)アプリケーションを作成します。
店舗IDをもとに、店舗詳細情報を取得する至ってシンプルな内容になっています。
パッケージの構成は以下の通り。

sample-eb-java
     |
    src/main/java
     |----jp.co.sample_eb_java
     |               |---- app
     |               |     |---- controller
     |               |     |---- response
     |               |
     |               |---- domain
     |               |      |---- service
     |               |      |---- repository
     |               |      |---- model
     |               |  
     |               |---- exception
     |               
     |
    src/main/resources
     |----application.yml
     |
    Buildfile
    Procfile
    build.gradle
    .ebextensions

・・・以下、省略

[1] プロジェクトの作成

(1) Eclipseを開いたら、「ファイル」(①)> 「新規」(②) > 「プロジェクト」(③)の順でクリック。
image.png

(2) ウィザードが立ち上がったら、「Springスターター・プロジェクト」を選択し(①)、「次へ」(②)をクリック。
image.png

(3) 以下のキャプチャのように設定し(①)、「次へ」(②)をクリック。
image.png

(4)Lombok, MySQL Driver, Spring Data JPA, Spring Webにチェックを入れ(候補に出ていない場合は検索ボックスより検索してください)、「次へ」(②)を押下
image.png

[2] 設定ファイルの変更・作成

次に、設定ファイルの変更と作成を行います。

(1)application.propertiesをapplication.ymlに名前変更し、以下のように記述します。
※application.propertiesのままでもいいのですが、最近の潮流(?)を踏まえてそうしました。
image.png

application.yml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${DB_HOST:ホスト名}:${DB_PORT:ポート番号}/${DB_NAME:データベース名}?serverTimezone=JST
    username: ${DB_USERNAME:ユーザー名}
    password: ${DB_PASSWORD:パスワード}
  jpa:
    database: MYSQL
    hibernate.ddl-auto: none
server:
  port: ${SERVER_PORT:サーバーポート番号}

日本語の箇所は後で変更するので、いったんこのままで。

(2)ルートディレクトリにBuildfileを作成し、以下の通り記述。

Buildfile
build: ./gradlew assemble

(3)ルートディレクトリにProcfileを作成し、以下の通り記述。

Procfile
web: java -jar build/libs/sample-eb-java.jar

(4) build.gradleに追記します。追記前の箇所を探し、「bootJar.archiveName = "sample-eb-java.jar"」を追記してください。

build.gradle(追記前)
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}
build.gradle(追記後)
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    bootJar.archiveName = "sample-eb-java.jar"
}

[3]ソースコードの作成

続いて、ソースコードの作成です。
パッケージ構成はこの章(1. Java(Spring Boot)アプリケーションの作成)の冒頭の通りですが、上まで戻るのも面倒かと思うので、キャプチャを貼っておきます。
image.png

(1)まずはEntityクラス。
後から出てきますが、データベースに作成するshop_informationsテーブルに対応する内容となっています。

ShopInformation.java
package jp.co.sample_eb_java.domain.model;

import java.io.Serializable;
import javax.persistence.*;

import lombok.Getter;
import lombok.Setter;


/**
 * 店舗情報テーブルに紐づくEntityクラス
 * 
 * @author CHI-3
 *
 */
@Entity
@Getter
@Setter
@Table(name="shop_informations")
@NamedQuery(name="ShopInformation.findAll", query="SELECT s FROM ShopInformation s")
public class ShopInformation implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="shop_id")
    private Integer shopId;

    private String access;

    private String address;

    @Column(name="business_hour")
    private String businessHour;

    @Column(name="regular_holiday")
    private String regularHoliday;

    @Column(name="shop_name")
    private String shopName;

    private String tel;

    @Column(name="zip_code")
    private String zipCode;

    public ShopInformation() {
    }

}

(2)続いて、Repositoryインターフェース。テーブル操作に使用するインターフェースです。

ShopInformationRepository.java
package jp.co.sample_eb_java.domain.repository;

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

import jp.co.sample_eb_java.domain.model.ShopInformation;

/**
 * 店舗情報を扱うリポジトリインターフェース
 *
 * @author CHI-3
 *
 */
@Repository
public interface ShopInformationRepository extends JpaRepository<ShopInformation, Integer>{

    /**
     * 店舗IDに紐づく店舗情報を取得
     *
     * @param shopId 店舗ID
     * @return 店舗情報
     */
    public ShopInformation findByShopId(Integer shopId);

}

(3)続いて、Serviceクラス。ロジックを提供します。

ShopInformationService.java
package jp.co.sample_eb_java.domain.service;

import java.util.Optional;

import org.springframework.stereotype.Service;

import jp.co.sample_eb_java.domain.model.ShopInformation;
import jp.co.sample_eb_java.domain.repository.ShopInformationRepository;
import lombok.RequiredArgsConstructor;

/**
 * 店舗情報を取得するサービスクラス
 *
 * @author CHI-3
 *
 */
@Service
@RequiredArgsConstructor
public class ShopInformationService {

    private final ShopInformationRepository shopInformationRepository;

    /**
     * 店舗情報を取得
     *
     * @param shopId 店舗ID
     * @return 店舗情報
     * @throws Exception
     */
    public ShopInformation getShopInformation(Integer shopId) throws Exception{
        // 店舗情報を取得:対象店舗が存在しなければ例外をThrow
        ShopInformation shopInformation = Optional.ofNullable(shopInformationRepository.findByShopId(shopId)).orElseThrow(Exception::new);
        return shopInformation;
    }

}

(4)次に、Responseクラス。返却する値の成形を行います。

ShopInformationResponse.java
package jp.co.sample_eb_java.app.response;

import jp.co.sample_eb_java.domain.model.ShopInformation;
import lombok.Builder;
import lombok.Getter;

/**
 * 店舗情報取得用レスポンスクラス
 * @author CHI-3
 *
 */
@Getter
@Builder
public class ShopInformationResponse {
    /** 店舗情報 */
    private ShopInformation shopInformation;
}

(5)お次は、Controllerクラス。JSON形式で値を返却するため、@RestControllerを使っています。

ShopInformationController.java
package jp.co.sample_eb_java.app.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import jp.co.sample_eb_java.app.response.ShopInformationResponse;
import jp.co.sample_eb_java.domain.model.ShopInformation;
import jp.co.sample_eb_java.domain.service.ShopInformationService;
import lombok.RequiredArgsConstructor;

/**
 * 店舗情報を取得するAPI
 * 
 * @author CHI-3
 *
 */
@RestController
@RequiredArgsConstructor
public class ShopInformationController {

    private final ShopInformationService shopInformationService;

    /**
     * 店舗情報を取得
     *
     * @param shopId 店舗ID
     * @return レスポンスエンティティ(店舗情報)
     * @throws Exception
     */
    @GetMapping("/shop-information/{shopId}")
    public ResponseEntity<ShopInformationResponse> getShopInformation(@PathVariable("shopId") Integer shopId) throws Exception{
        ShopInformation shopInformation = shopInformationService.getShopInformation(shopId);
        ShopInformationResponse shopInformationResponse = ShopInformationResponse.builder().shopInformation(shopInformation).build();
        return new ResponseEntity<>(shopInformationResponse, HttpStatus.OK);
    }

}

(6) 最後に、ExceptionHandler。エラーハンドリングしてくれるクラスです。
今回は、存在しない店舗IDをリクエストした際などに、400エラー(Bad Request)を返却する内容になっています。

ExceptionHandler.java
package jp.co.sample_eb_java.exception;


import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import lombok.extern.slf4j.Slf4j;

/**
 * 例外ハンドラー
 *
 * @author CHI-3
 *
 */
@ControllerAdvice
@Slf4j
public class ExceptionHandler {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler({Exception.class})
    public @ResponseBody
    ResponseEntity<Object> handleError(final Exception e) {
        log.info("call ExceptionHandler", e);
        return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
}

2. Gitリポジトリの作成

手順1で作成したプロジェクトをGit管理できるようにします。
手順1で作成したプロジェクト上でローカルリポジトリを作成し、リモートリポジトリと同期する流れで行います。

[1] ローカルリポジトリの作成

(1) コマンドプロンプト、またはGitBashを開き、1で作成したプロジェクトのルートディレクトリに移動(cd)した後、リポジトリの初期化を行います(以下のコマンドを実行)。

> git init

プロジェクトのルートディレクトリに.gitフォルダができればOK。
image.png

(2)コミットする前に、 .gitignoreの書き換えを行います。
デフォルトの記述では、STSなど、必要な機能がコミット対象外となってしまい、後々動作しないなど、不具合の原因になりかねません。
以下の通り記述を変更します。
classファイルやlockファイルなど、コミットの必要のないものが対象外となります。

.gitignore
bin/*
.lock

(3)編集内容をコミットします。
以下のコマンドを順に実行すればOK。

> git add .
> git commit -m "first commit"

「first commit」の部分(コミットメッセージ)は任意の内容でOKですが、編集内容がわかるメッセージにしましょう(以降同様。)

(4)再度、.gitignoreを編集します。
機能としては必要だけれども、ローカルからのコミット対象外となるものを記述。

.gitignore
# Created by https://www.toptal.com/developers/gitignore/api/java,gradle,eclipse
# Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,eclipse

### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders

# External tool builders
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
*.launch

# PyDev specific (Python IDE for Eclipse)
*.pydevproject

# CDT-specific (C/C++ Development Tooling)
.cproject

# CDT- autotools
.autotools

# Java annotation processor (APT)
.factorypath

# PDT-specific (PHP Development Tools)
.buildpath

# sbteclipse plugin
.target

# Tern plugin
.tern-project

# TeXlipse plugin
.texlipse

# STS (Spring Tool Suite)
.springBeans

# Code Recommenders
.recommenders/

# Annotation Processing
.apt_generated/
.apt_generated_test/

# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet

# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project

### Eclipse Patch ###
# Spring Boot Tooling
.sts4-cache/

### Java ###
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

### Gradle ###
.gradle
build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Cache of project
.gradletasknamecache

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties

### Gradle Patch ###
**/build/

# End of https://www.toptal.com/developers/gitignore/api/java,gradle,eclipse

※上記はgitignore.ioで、

  • Java
  • Gradle
  • Eclipse

を条件として作成しました。

変更が完了したら、コミットしておきましょう。

> git add .gitignore
> git commit -m "fix .gitignore"

(5) gradlewの権限変更を行います。
【重要】この手順を飛ばしてしまうと、ビルド、デプロイ時にエラーになるので、忘れず実施してください。

まず、gradlewの権限を確認します。

> git ls -files -s gradlew
100644 fbd7c515832dab7b01092e80db76e5e03fe32d29 0       gradlew

上記のままだと、読み取り権限しかないので、以下のコマンドを実行して、実行権限を与えます。

> git update-index --add --chmod=+x gradlew

実行が完了したら、再度権限確認を行いましょう。
以下の通り、最初の6桁の数字の下3桁が755になっていればOK。

> git ls-files -s gradlew
100755 fbd7c515832dab7b01092e80db76e5e03fe32d29 0       gradlew

変更内容をコミットします。

> git add gradlew
> git commit -m "fix permission of gradlew"

[2] リモートリポジトリの作成

(1) AWSマネジメントコンソールにログインし、「サービス」(①)からCodeCommitを探してクリック(②)。
image.png

(2) CodeCommitのページに遷移したら、「リポジトリを作成」をクリック。
image.png

(3)手順1で作成したプロジェクトと同名のリポジトリ名をつけ(①)、「作成」をクリック。
image.png

[3] ローカルリポジトリとリモートリポジトリを同期

(1)リモートリポジトリの作成が完了すると、以下のようなページに遷移するので、「URLのクローン」(①) > 「HTTPSのクローン」(②)をクリック。
image.png

(2)コマンドプロンプト or GitBashを開き、プロジェクトのルートディレクトリにて、以下のコマンドを実行。

> git remote add origin (1)の「HTTPSのクローン」でコピーされたURI

(3) 内容をリモートリポジトリのmasterにプッシュ。

> git push -u origin master

(4)コンソールにて、リモートリポジトリにローカルリポジトリの内容が反映されていることを確認しましょう。
image.png

続編について

続きは、

にて。
3. Elastic Beanstalk環境の作成, 4. データベースの接続設定を行います。

参考

環境

手順

1. Java(Spring Boot)アプリケーションの作成

2. Gitリポジトリの作成

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

Retrofit2とOkhttp3とGsonを使用してAPIを作成(Java)

API作成時の備忘録。
細かい説明は抜きにして、とりあえずこれで通信できた。

手順

  • manifestfileに通信を許可するように設定
  • リクエスト、レスポンスを受け取る用のクラスを定義
  • エンドポイントの作成
  • HTTPクライアントの作成
  • 実行

manifestFileに通信を許可するように設定

AndroidManifest.xml に以下を記述する

AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />

リクエスト、レスポンスを受け取る用のクラスを定義

ログイン用のリクエストクラス、 requestのbodyがこの形式で送られる

LoginRequest.java
package com.example.bookmanager_android.models.requests;

public class LoginRequest {
    private String email;
    private String password;

    public LoginRequest(String email, String passWord){
        this.email = email;
        this.password = passWord;
    }
}
  • レスポンスクラス

ログイン用のレスポンスクラス、responseがこの形で受け取られる

LoginResponse.java
package com.example.bookmanager_android.models.requests.response;

public class LoginResponse {
    public int status;
    public Result result;

    public static class Result {
        public int id;
        public String email;
        public String token;
    }
}

エンドポイントを作成する

interfaceを作成

RequestApiService
public interface RequestApiService {
    @POST("/login") // API エンドポイント
    Call<LoginResponse> logIn(@Body LoginRequest requestBody); 
    // logInメソッドが呼ばれると,body形式は上記で作った、LoginRequestクラスの形になる
}

HTTPクライアントの作成

HttpClient.java
public class HttpClient {
    public static RequestApiService apiService;

    private static final String URL = "http://baseurl"; // BaseUrlを指定

    // Okhttp
    private static final OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build();
    // GSON
    private static final Gson gson = new GsonBuilder()
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .create();

    // RetrofitにGSONとOkhttpを組み合わせてHttpクライアントの作成
    public static RequestApiService getRequestApiService() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(client)
                .build();
        apiService = retrofit.create(RequestApiService.class);
        return apiService;
    }

実行

SignInFragment.java
private boolean starLogin(String emailStr, String passwordStr) {
        // bodyの作成
        LoginRequest requestBody = new LoginRequest(emailStr, passwordStr);
        // HTTPクライアントの呼び出し
        RequestApiService apiService = HttpClient.getRequestApiService();

        // 通信開始 interfaceで定義した処理が呼ばれる
        apiService.logIn(requestBody).enqueue(new Callback<LoginResponse>() {
            @Override
            public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
                if (response.isSuccessful()) {
                   // 通信処理成功時の処理を記載
                } else {
                    Log.e("SignInFragment", "Something wrong On Response: " + response.toString());   
                }
            }

            @Override
            public void onFailure(Call<LoginResponse> call, Throwable t) {
                // 通信処理失敗時の処理を記載
            }
        });
        return true;
    }

所感

tokenの保持とか、成功失敗時の処理とかまだあるが、とりあえずこれで通信はできた。

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

[Java]MinecraftのModを作成しよう 1.14.4【6. レシピの追加】

(この記事は一連の解説記事の一つになります)

先頭記事:入門編
前の記事:5. 防具の追加
次の記事:7. 進捗の追加

レシピの追加

さて、1. アイテムの追加2. ブロックの追加4. ツールの追加5. 防具の追加と各種アイテムを追加してきましたが、これらをクラフトするレシピがまだありません。今回はいくつかのパターンのレシピを追加していきます。
1.14.4においてレシピの追加はコードを書く必要はなく、jsonベースで追加できます。

定型レシピ

例えば剣のレシピを追加してみましょう。

\src\main\resources
   ├ assets
   └ data
      └ example_mod
         ├ loot_tables
         └ recipes
            └ example_sword.json

\src\main\resources\data\recipesフォルダを作り、その中にレシピファイルを配置します。

example_sword.json
{
  "type": "minecraft:crafting_shaped",
  "pattern": [
    "#",
    "#",
    "X"
  ],
  "key": {
    "#": {
      "item": "example_mod:example_ingot"
    },
    "X": {
      "item": "minecraft:stick"
    }
  },
  "result": {
    "item": "example_mod:example_sword"
  }
}

typeminecraft:crafting_shapedを与えることで、定型レシピを作ることができます。定型レシピとはすなわち、素材の配置が決まっているクラフトのレシピです。
keyで任意のトークン(ここでは#とXですが、何でもよいです)と対応するアイテムを決定し、このトークンを用いてpatternに3×3以下の行列のような形で配置を決定します。
resultにクラフトの結果得られるアイテムを指定します。
キャプチャ.PNG
剣が作れるようになりました。
この例では素材の配置に1列しか使っていないため、図のようにどの位置で縦に並べても作ることができます。
半角空白で「何もない」を示すので、明示的に以下のようにしてもいいですが、この場合でも左あるいは右の縦1列でも作れるようになるようです。

  "pattern": [
    " # ",
    " # ",
    " X "
  ]

不定形レシピ

次に素材の配置に指定のない不定形レシピを追加してみましょう。
例としてはやや不適ですが、ブロック2つからインゴット18個に分解するレシピを考えてみます。

\src\main\resources
   ├ assets
   └ data
      └ example_mod
         ├ loot_tables
         └ recipes
            └ example_ingot.json
example_ingot.json
{
  "type": "minecraft:crafting_shapeless",
  "ingredients": [
    {
      "item": "example_mod:example_block"
    },
    {
      "item": "example_mod:example_block"
    }
  ],
  "result": {
    "item": "example_mod:example_ingot",
    "count": 18
  }
}

typeminecraft:crafting_shapelessを与えることで、不定型レシピを作ることができます。
ingredientsは材料の配列で、ここにクラフトに使うアイテムを列挙します。
resultは定型レシピ同様です。countで個数を指定できます(デフォルト1のため前項では省略していました)。
キャプチャ.PNG
任意の配置からインゴットが得られました。

精錬レシピ

続いてかまどによる精錬のレシピを追加します。例えば、ダイヤモンドを精錬したらインゴットが得られるようにしましょう。

\src\main\resources
   ├ assets
   └ data
      └ example_mod
         ├ loot_tables
         └ recipes
            └ furnace
               └ example_ingot.json

(recipes以下のファイルは全部登録してくれるようなので、多分フォルダ構成やファイル名は自由でいいと思います。)

example_ingot.json
{
  "type": "minecraft:smelting",
  "ingredient": {
    "item": "minecraft:diamond"
  },
  "result": {
    "item": "example_mod:example_ingot"
  },
  "experience": 3.0,
  "cookingtime": 200
}

typeminecraft:smeltingを指定して精錬レシピを追加します。
ingredient(単数なので注意)は材料、resultが精錬後のアイテムです。
experienceは精錬で得る経験値、cookingtimeは精錬時間です。ちなみにバニラの鉄がそれぞれ0.7, 200です。
キャプチャ.PNG
精錬できました。

発展

Q. レシピブックに出てないんだけど?
A. 一度作れば載ります。先に乗せたい場合advancements(実績)にかかわる部分で手を加える必要がありますが、まだ私も学習していないので、後に追記できればと思います。


Q. 複数の素材から同じアイテムを作りたい
A. 3つの解決法があります。
1つ目は単純にレシピのjsonファイルをその数だけ作る方法です。推奨しません。
2つ目はkeyあるいはingradientにアイテムを複数渡す方法です。

Xの位置はstickかoak_woodのどちらか
{
  "type": "minecraft:crafting_shaped",
  "pattern": [
    "#",
    "#",
    "X"
  ],
  "key": {
    "#": {
      "item": "example_mod:example_ingot"
    },
    "X": [
      {
        "item": "minecraft:stick"
      },
      {
        "item": "minecraft:oak_wood"
      }
    ]
  },
  "result": {
    "item": "example_mod:example_sword"
  }
}
材料はdiamondかgold_ingotのどちらか
{
  "type": "minecraft:smelting",
  "ingredient": [
    {
      "item": "minecraft:diamond"
    },
    {
      "item": "minecraft:gold_ingot"
    }
  ],
  "result": {
    "item": "example_mod:example_ingot"
  },
  "experience": 3.0,
  "cookingtime": 200
}

(shapelessなレシピについてはこの手法が使えるか不明でした。使えなさそう?)

3つ目はtagを使う方法です。例えばバニラの木炭などは、あらゆる種類の原木から1つのアイテムをクラフトしています。以下にコードを示します。

charcoal.json
{
  "type": "minecraft:smelting",
  "ingredient": {
    "tag": "minecraft:logs"
  },
  "result": "minecraft:charcoal",
  "experience": 0.15,
  "cookingtime": 200
}

このタグはnet\minecraft\tags\BlockTags.javaで宣言されています。もちろん自分で新しいタグを定義することも可能なので、代替可能な素材をまとめたタグを作り、それを使ってレシピを定めるのはよい方法でしょう。


Q. バニラのレシピを改変したい
A. 追加は容易です。変更は困難。
つまり、A→Bのクラフトに対して、C→Bを追加することは簡単ですが、A→Dのクラフトに変更することは難しいです。前者は単に新しくレシピを追加すればバニラのものと共存しますが、後者は同じ素材からなるレシピを追加してもどうやらバニラのほうが優先されるようです。そのため、レシピを登録するコードの方までさかのぼって手を加える必要がありそうです。


Q. 醸造のレシピを作りたい
恐らくですが、醸造のレシピについては上記のようなjsonベースの追加ができません。(参考)

参考

Recipes - Forge Documentation
Minecraft 1.14.4 Forge Modの作成 その9 【レシピの追加】

次の記事

7. 進捗の追加

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

Javaアノテーションの歴史

Javaのアノテーションの役割と歴史をまとめる。

歴史

「@」アノテーションとは、Java 1.5で導入された、

  • @Overrideのようにコンパイラにヒントを与えるコメントとして生まれたのがきっかけ。

アノテーションを使用するメリット。

  • コードの正確性:@Nullable@Deprecated@Overrideなどのアノテーションにより、メソッドやフィールドに重要な意味情報を追加し、コンパイラによってその情報が強制されるようにすることができる。

  • テスト・メソッド:TestNGとアノテーションが登場するまで、JUnitは リフレクションを使用してメソッドがテスト・メソッドであることを判別していました。この判別には、特別なネーミング規則が必要でした。
    アノテーションの採用によって、ネーミング規則を使用する必要がなくなった。

  • 永続性(Hibernateなど):アノテーションを付加することで、フィールドやメソッドをデータベースに保存されているデータと紐付けることができる。

  • 依存性注入:インジェクションする必要があるクラスにアノテーションを付加し、その旨を示すことができる。

よく使うアノテーション

@Nullable@Nonnull(javax.annotation)

  • フィールドおよびメソッド・パラメータに付加でき、変数がnull値を取ることができるか どうかを指定します。
  • このアノテーションは非常に有用であり、Javaコンパイラ(javac)を使用する多くのツール(主要なIDEが代表的な例です)がこの2つを認識します。
  • 可能な限り、常にこのアノテーションを使用するようにします。コードベースにおけるnullポインタ例外の数が急激に減ることにすぐ気付くはずです。

@Override

Java6以降必須になっている。

理由

  • このアノテーションは、親インタフェースまたは親クラスのメソッドをオーバーライドするすべてのメソッドに付加する必要がある。
  • この付加によって、意図せずにメソッドをオーバーライドしてしまうこと

  • 逆にタイプ・ミスのためにオーバーライドしていなかったメソッドを「オーバーライドした」と思ってしまう事態を防ぐことができる。

参考にした記事(いつもありがとうございます)

https://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-MA16-Annotations.pdf

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

[自分用メモ]Springフレームワークについて

自己紹介

Javaを勉強したての社会人一年目です。
業務でspringフレームワークを使用しているので、メモとしてまとめています。

Javaに関するアドバイス等募集中です。

Springフレームワークとは

JavaでWebシステムを開発する時にとても便利なフレームワーク(簡単に言いすぎてるかな笑笑)。

springMVC、springsecurity...等、spring〇〇がいっぱいある。

構成(処理の流れ)

くそ雑な図ですが笑笑

springmvc-1.jpg

クライアント側
1.HTMLファイルやJSPファイル

サーバ側(2~6はJavaファイル)
2.controller
 画面処理等

3.service
 DBから取得した値を使ったりする処理等

4.form
 1で入力された値を保持する場所

5.entity またはdomain
 DBから取得した値を保持する場所

6.mapper
 DBと接続する

7.xmlファイル
 SQLを記述

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

[自分用メモ]よく使うJava文法 随時更新

自己紹介

Javaを勉強したての社会人一年目です。
Javaの文法についてメモみたいな感じでまとめています。
文法については、「調べるのめんどくせーw」「覚えるのめんどくせーw」って感じなので、完全個人用メモです笑笑
※業務等でこれからよく使ったり、便利な文法が見つかり次第更新する予定です。

Javaに関するアドバイス等募集中です。

文字列分割

String str = new String("星野みなみ,齋藤飛鳥,堀未央奈");
String[] oshi = str.split(",");

// 出力
for (int i=0; i < oshi.length; i++) {
  System.out.println(oshi[i]);
}
出力
星野みなみ
齋藤飛鳥
堀未央奈

文字列比較

String str1 = "星野みなみ";
String str2 = "星野みなみ";
String str3 = "寺田蘭世";

// 比較
// str1とstr2
if (str1.equals(str2)) {
  System.out.println("等しい");
} else {
  System.out.println("等しくない");
}

// 比較
// str1とstr3
if (str1.equals(str3)) {
  System.out.println("等しい");
} else {
  System.out.println("等しくない");
}

出力
等しい
等しくない

正規表現

日付
文字列ymdに格納された日付が、YYYY/MM/DDの形になっているかを判定。

Pattern p = Pattern.compile("^[0-9]{4}/[0-9]{2}/[0-9]{2}$");
Matcher m = p.matcher(ymd);
m.find(); // マッチしてたらtrue,マッチしてなかったらfalse

時間
文字列timeに格納された時間が、HH:mmの形になっているかを判定。

Pattern p = Pattern.compile("^[0-9]{2}:[0-9]{2}$");
Matcher m = p.matcher(time);
m.find(); // マッチしてたらtrue,マッチしてなかったらfalse

その他の正規表現

// URL
Pattern p = Pattern.compile("^https?://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$");

// ドメイン名
Pattern p = Pattern.compile("^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z-]{2,}$");

// 固定電話番号
Pattern p = Pattern.compile("^0\d(-\d{4}|\d-\d{3}|\d\d-\d\d|\d{3}-\d)-\d{4}$");

// 携帯電話番号
Pattern p = Pattern.compile("^0[789]0-\d{4}-\d{4}$");

// フリーダイヤル
Pattern p = Pattern.compile("^(0120|0800)-\d{3}-\d{3}$");

// 郵便番号
Pattern p = Pattern.compile("^\d{3}-\d{4}$");

日付、時間フォーマット指定

SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd (E)", Locale.JAPANESE);
SimpleDateFormat de = new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat dd = new SimpleDateFormat("HH:mm");
SimpleDateFormat dc = new SimpleDateFormat("HH:mm:ss");

現在の日時を取得

現在の日時を取得し、Todayに格納

SimpleDateFormat de = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

// 現在の日付を取得
Date today = new Date();
today.toString();
String Today = de.format(today);

日付比較( equal()、after()、before() )

public void dateTodate() throws ParseException {
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
  Date date1 = sdf.parse("2020/04/01");
  Date date2 = sdf.parse("2020/04/30");
  Date date3 = sdf.parse("2020/04/01");

  if (date1.equals(date3)) {
    System.out.println("date1とdate3は同じ日");
  }

  if (date1.before(date2)) {
    System.out.println("date1はdate2に比べて過去");
  }

  if (date2.after(date1)) {
    System.out.println("date2はdate1に比べて未来");
  }
}
出力
date1とdate3は同じ日
date1はdate2に比べて過去
date2はdate1に比べて未来

閏年判定

変数ymd_YYY(西暦年)が閏年かどうかを判定。

// 閏年判定
if (Integer.parseInt(ymd_YYYY) % 4 == 0) {
    if ((Integer.parseInt(ymd_YYYY) % 100) == 0) {
        if ((Integer.parseInt(ymd_YYYY) % 400) == 0) {
            // うるう年
        }
    } else {
        // うるう年
    }
}

2次元配列のコピー

String[][] oshi = { {"星野みなみ", "齋藤飛鳥", "堀未央奈"}, {"みなみ", "あすか", "みおな"}, {"千葉県", "東京都", "岐阜県"}};
String[][] oshi_copy = new String[oshi.length][];

for (int i=0; i < oshi.length; i++) {
    oshi_copy[i] = new String[oshi[i].length];

    for (int j=0; j < oshi[i].length; j++) {
        oshi_copy[i][j] = oshi[i][j];
    }
}

// 出力
for (int i=0; i < oshi_copy.length; i++) {
    for (int j=0; j < oshi_copy[i].length; j++) {
        System.out.println(oshi_copy[i][j]);
    }
}
出力
星野みなみ 
齋藤飛鳥 
堀未央奈 
みなみ 
あすか 
みおな 
千葉県 
東京都 
岐阜県 

for文(for、拡張for、foreach)

// for
// スタンダード
for (int i=0; i < 10; i++) {
  // 処理
}

// 拡張for
// 配列やリストのすべての要素に順番に処理を行う場合に使ったりする
for (String str : Array) {
  // 処理
}

// forEach
// 上2つより簡潔に書くことが可能(自分は完全に理解しきれていないが...)
str.forEach( i -> // 処理 );

おわりに

みなさん的によく使うJava文法とかありましたら、コメント欄にて募集中です笑笑

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

[自分用メモ]いちいち調べるのめんどいので、Java(文法)まとめた

自己紹介

Javaを勉強したての社会人一年目です。
Javaの文法についてメモみたいな感じでまとめています。
文法については、「調べるのめんどくせーw」「覚えるのめんどくせーw」って感じなので、完全個人用メモです笑笑
※業務等でこれからよく使ったり、便利な文法が見つかり次第更新する予定です。

Javaに関するアドバイス等募集中です。

文字列分割

String str = new String("星野みなみ,齋藤飛鳥,堀未央奈");
String[] oshi = str.split(",");

// 出力
for (int i=0; i < oshi.length; i++) {
  System.out.println(oshi[i]);
}
出力
星野みなみ
齋藤飛鳥
堀未央奈

正規表現

日付
文字列ymdに格納された日付が、YYYY/MM/DDの形になっているかを判定。

Pattern p = Pattern.compile("^[0-9]{4}/[0-9]{2}/[0-9]{2}$");
Matcher m = p.matcher(ymd);
m.find(); // マッチしてたらtrue,マッチしてなかったらfalse

時間
文字列timeに格納された時間が、HH:mmの形になっているかを判定。

Pattern p = Pattern.compile("^[0-9]{2}:[0-9]{2}$");
Matcher m = p.matcher(time);
m.find(); // マッチしてたらtrue,マッチしてなかったらfalse

日付、時間フォーマット指定

SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd (E)", Locale.JAPANESE);
SimpleDateFormat de = new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat dd = new SimpleDateFormat("HH:mm");
SimpleDateFormat dc = new SimpleDateFormat("HH:mm:ss");

閏年判定

変数ymd_YYY(西暦年)が閏年かどうかを判定。

// 閏年判定
if (Integer.parseInt(ymd_YYYY) % 4 == 0) {
    if ((Integer.parseInt(ymd_YYYY) % 100) == 0) {
        if ((Integer.parseInt(ymd_YYYY) % 400) == 0) {
            // うるう年
        }
    } else {
        // うるう年
    }
}

2次元配列のコピー

String[][] oshi = { {"星野みなみ", "齋藤飛鳥", "堀未央奈"}, {"みなみ", "あすか", "みおな"}, {"千葉県", "東京都", "岐阜県"}};
String[][] oshi_copy = new String[oshi.length][];

for (int i=0; i < oshi.length; i++) {
    oshi_copy[i] = new String[oshi[i].length];

    for (int j=0; j < oshi[i].length; j++) {
        oshi_copy[i][j] = oshi[i][j];
    }
}

// 出力
for (int i=0; i < oshi_copy.length; i++) {
    for (int j=0; j < oshi_copy[i].length; j++) {
        System.out.println(oshi_copy[i][j]);
    }
}
出力
星野みなみ 
齋藤飛鳥 
堀未央奈 
みなみ 
あすか 
みおな 
千葉県 
東京都 
岐阜県 

for文(for、拡張for、foreach)

// for
// スタンダード
for (int i=0; i < 10; i++) {
  // 処理
}

// 拡張for
// 配列やリストのすべての要素に順番に処理を行う場合に使ったりする
for (String str : Array) {
  // 処理
}

// forEach
// 上2つより簡潔に書くことが可能(自分は完全に理解しきれていないが...)
str.forEach( i -> // 処理 );

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

[自分用メモ]よく使うJava文法募集中

自己紹介

Javaを勉強したての社会人一年目です。
Javaの文法についてメモみたいな感じでまとめています。
文法については、「調べるのめんどくせーw」「覚えるのめんどくせーw」って感じなので、完全個人用メモです笑笑
※業務等でこれからよく使ったり、便利な文法が見つかり次第更新する予定です。

Javaに関するアドバイス等募集中です。

文字列分割

String str = new String("星野みなみ,齋藤飛鳥,堀未央奈");
String[] oshi = str.split(",");

// 出力
for (int i=0; i < oshi.length; i++) {
  System.out.println(oshi[i]);
}
出力
星野みなみ
齋藤飛鳥
堀未央奈

正規表現

日付
文字列ymdに格納された日付が、YYYY/MM/DDの形になっているかを判定。

Pattern p = Pattern.compile("^[0-9]{4}/[0-9]{2}/[0-9]{2}$");
Matcher m = p.matcher(ymd);
m.find(); // マッチしてたらtrue,マッチしてなかったらfalse

時間
文字列timeに格納された時間が、HH:mmの形になっているかを判定。

Pattern p = Pattern.compile("^[0-9]{2}:[0-9]{2}$");
Matcher m = p.matcher(time);
m.find(); // マッチしてたらtrue,マッチしてなかったらfalse

日付、時間フォーマット指定

SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd (E)", Locale.JAPANESE);
SimpleDateFormat de = new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat dd = new SimpleDateFormat("HH:mm");
SimpleDateFormat dc = new SimpleDateFormat("HH:mm:ss");

閏年判定

変数ymd_YYY(西暦年)が閏年かどうかを判定。

// 閏年判定
if (Integer.parseInt(ymd_YYYY) % 4 == 0) {
    if ((Integer.parseInt(ymd_YYYY) % 100) == 0) {
        if ((Integer.parseInt(ymd_YYYY) % 400) == 0) {
            // うるう年
        }
    } else {
        // うるう年
    }
}

2次元配列のコピー

String[][] oshi = { {"星野みなみ", "齋藤飛鳥", "堀未央奈"}, {"みなみ", "あすか", "みおな"}, {"千葉県", "東京都", "岐阜県"}};
String[][] oshi_copy = new String[oshi.length][];

for (int i=0; i < oshi.length; i++) {
    oshi_copy[i] = new String[oshi[i].length];

    for (int j=0; j < oshi[i].length; j++) {
        oshi_copy[i][j] = oshi[i][j];
    }
}

// 出力
for (int i=0; i < oshi_copy.length; i++) {
    for (int j=0; j < oshi_copy[i].length; j++) {
        System.out.println(oshi_copy[i][j]);
    }
}
出力
星野みなみ 
齋藤飛鳥 
堀未央奈 
みなみ 
あすか 
みおな 
千葉県 
東京都 
岐阜県 

for文(for、拡張for、foreach)

// for
// スタンダード
for (int i=0; i < 10; i++) {
  // 処理
}

// 拡張for
// 配列やリストのすべての要素に順番に処理を行う場合に使ったりする
for (String str : Array) {
  // 処理
}

// forEach
// 上2つより簡潔に書くことが可能(自分は完全に理解しきれていないが...)
str.forEach( i -> // 処理 );

おわりに

みなさん的によく使うJava文法とかありましたら、コメント欄にて募集中です笑笑

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

【Java】splitメソッド

splitメソッドは、
Stringで定義した区切り文字列を分割することで、
文字列の配列に分ける。
(文字列を指定された正規表現に一致する位置で分割する)

サンプルコード

String str = "りんご,レモン,すいか,ブドウ"

String[] fruit = str.split(",", 0);

/**
*結果:
*fruit[0] = "りんご"
*fruit[1] = "レモン"
*fruit[2] = "すいか"
*fruit[3] = "ブドウ"
*/

最後の0のところは、
分割する回数を指す。
負の値を指定すると制限なし。

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