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

Progate無料版をやってみる【Java II】

前回に引き続きProgate無料レッスンをやっていこうと思います。

今回はJava II です。
ここまでが無料分です。

レッスン一覧

Java II

公式レッスン

真偽値・比較演算

比較演算子(2)

論理演算子

・基本的なことなので・・・省略

if文

・基本的なことですが・・・
 少しは載せとかないと・・・

image.png

波線があるところは「デッドコード」死にコードと出ています。
その名の通り、falseなので、絶対にifブロック内の処理は実行されません。

コマンドプロンプトからの実行結果
image.png

else, else if

・javaはelse ifと書く。
 elsifより全然しっくりくる・・・。
他記事で指摘いただいたのですが、else ifは1つの条件分岐ではなく、
elseifが連続していてそれを1行で書いているだけなのだそうです。

// これを
if (x == 8) {
  System.out.println("8です");
} else {
  if (x > 8) {
    System.out.println("8より大きいです");
  } else {
    System.out.println("8より小さいです");
  }
}

// ↓ 以下のようにelseの {} を省略して書いているだけ 

if (x == 8) {
  System.out.println("8です");
} else if (x > 8) {
    System.out.println("8より大きいです");
  } else {
    System.out.println("8より小さいです");
  }

// ※あえてインデント揃えてないです。

なので、RubyのelsifやPythonのelifとは別物と理解しました。
処理の結果は同じになるのでしょうけど。

ちなみにjavaは文字列比較はequalsを使った方がいいです。

var test = new String("test");
System.out.println(test == "test"); // falseになる

System.out.println(test.equals("test")); // trueになる

==は格納されているメモリの参照先を比較しています。
var test = new String("test")でまずメモリに格納されます。
System.out.println(test == "test");"test"のところで①とは別のところに格納されます。
よって違うと判断されます。

なので、

var test = new String("test");
var test2 = test;
System.out.println(test == test2 ); // trueになります。

これはtrueになります。参照先が同じなので。

また、すごいややこしんですけど、

var test = "test";
System.out.println(test == "test"); // trueになる

trueになります。
どうも、リテラル"test"とした場合には、同じ文字列の場合、同じところを参照するようになるそうです。
ややこしい・・・。

とりあえず、equalsを使用していれば間違えないです。

switch文・switch文(2)

・基本的なこと。
casedefaultbreak;

while文・for文

・javaのforもオーソドックスな

for (var i = 0; i < count; i++) {
}

の形。レッスンだとvar使うと怒られるけど・・・。

break, continue

・breakループを抜ける。
・continue スキップ、次の要素。

配列を使ってみよう

new String[] {"test","bbb"};

初期化。

{"test", "bbb"};

でもいけるみたい・・・。
知らなかった・・・。C#はたぶん無理・・・。

配列の要素の上書き・配列と繰り返し

・特になし・・・。

拡張for文

・C#でいうところのforeach

var numbers2 = new int[]{1, 2, 3, 4};
for (var number2 : numbers2) {
}

総合課題

演習

package jp.test.testproject;

public class ProgateTest {

    public static void main(String[] args) {
        // 変数numbersに、与えられた数字の配列を代入してください
        int[] numbers = {1, 4, 6, 9, 13, 16};

        int oddSum = 0;
        int evenSum = 0;

        // for文を用いて、配列numbersの偶数の和と奇数の和を求めてください
        for (int number : numbers) {
          if(number % 2 == 0) {
            evenSum += number;
          } else {
            oddSum += number;
          }
        }

        System.out.println("奇数の和は" + oddSum + "です");
        System.out.println("偶数の和は" + evenSum + "です");
      }
}

結果
image.png

クリアしました
image.png

感想

やばい・・・ほとんど中身のない記事になってしまった・・・。
基本的なこと過ぎて、今までやってきた他言語の入門編で事足りてしまいます。

次回はPython Iをやっていきたいと思います。

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

Swift Network.framework Study 20200319「Receive and Send」

Study

Network.framework
Study:Receive and Send

環境

Client:Java、NetBeans
Server:Swift、Xcode

Server Source Swift

main.swift

import Foundation
import Network

var receiveANdSend = ReceiveAndSend()
receiveANdSend.startListener()
while receiveANdSend.running {
    sleep(1)
}

ReceiveAndSend.swift

import Foundation
import Network

class ReceiveAndSend {
    public var running = true

    func startListener() {
        let myQueue = DispatchQueue(label: "ExampleNetwork")

        do {
            let nWListener = try NWListener(using: .tcp, on: 7777)
            nWListener.newConnectionHandler = { (newConnection) in
                print("New Connection!!")
                newConnection.start(queue: myQueue)
                self.receive(nWConnection: newConnection)
            }
            nWListener.start(queue: myQueue)
            print("start")
        }
        catch {
            print(error)
        }
    }

    func receive(nWConnection:NWConnection) {
        nWConnection.receive(minimumIncompleteLength: 1, maximumLength: 5, completion: { (data, context, flag, error) in
            print("receiveMessage")
            if let data = data {
                let receiveData = [UInt8](data)
                print(receiveData)
                print(flag)
                self.sendMessage(nWConnection)
                if(flag == false) {
                    self.receive(nWConnection: nWConnection)
                }
            }
            else {
                print("receiveMessage data nil")
            }
        })
    }

    func sendMessage(_ connection: NWConnection) {
        let data = "Answer".data(using: .utf8)
        let completion = NWConnection.SendCompletion.contentProcessed { (error: NWError?) in
            print("応答送信完了")
            self.running = false
        }
        connection.send(content: data, completion: completion)
    }
}

Client Source Java

public class ExampleSendAndReceive {
   public static void main(String[] args) {
        try(Socket socket = new Socket("localhost", 7777);
            PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);) {

            Runnable runnable = () -> {
                System.out.println("Receive start");
                try {
                    int data = socket.getInputStream().read();
                    while(data != -1) {
                        System.out.print(data + " ");
                        data = socket.getInputStream().read();
                    }
                    System.out.println("end");
                }
                catch(Exception e) {
                    System.out.println(e);
                }
            };
            Thread thread = new Thread(runnable);
            thread.start();

            for(int i = 0; i < 5; i++) {
                printWriter.println("12345");
                Thread.sleep(5000);
            }
        }
        catch(Exception e) {
            System.out.println(e);
        }
    }    
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IntelliJ IDEAでブレークポイント設定しているのにデバッグで止まらないとき【Gradle】

要約

「Preferences」→「Build, Execution, Deployment」→「Build Tools」→「Gradle」
の「Build and run」の設定を「Gradle」から「IntelliJ」に変更する。

スクリーンショット 2020-03-19 19.52.55.png


IntelliJ IDEAを使っているが、Javaの開発とかにはそこまで慣れてない。
デバッグのためにブレークポイント設定して実行しても全然止まってくれない。
ということで調べた。

上記要約にある通り、IntelliJからの実行でないとプロセスにそのままアタッチできないとのことで、
実行が止まらない。
今回はGradleだったが、Mavenとかでも同じみたい。
(調べて見つけたのはMavenの記事だった)

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

初心者から始めるJava、インターフェイス

はじめに

この記事は備忘録である。
参考書レベルの内容だが、本記事に掲載するコードについては、
間違えたものが中心となる。これは実際にコーディング中に間違えた部分を掲載し、自分で反省するために投稿するという目的によるもの。
また、後日にJavaSilver試験問題の勉強を兼ねて復習するため、深い部分の話はここでは触れない。

環境

言語:Java11、JDK13.0.2
動作環境:Windows10

インターフェイス

前回扱った抽象クラスと似た使い方をする、インターフェイスという機構がある。抽象クラスはクラスの一種だった、インターフェイスも似た目的で作られるがその宣言・実装方法は異なる。

declareInterface.java
interface Felidae
{
  String type = cats;
  void meetsCats();
}

いつものclass宣言がなくなり、代わりにinterfaceになっている。クラス同様、フィールドとメソッドを持つことが出来る(コンストラクタを持つことはできない)が、
Felidae cat=new Felidae();のようなオブジェクトの生成はできない。またvoid meetsCats()は抽象メソッドのため、Felidaeインターフェイスを実装したクラスによってその具象メソッドを用意する必要がある(オーバーライド)。

String type = cats;はインターフェイスのフィールドだが、これは定数であり変更が効かない。インターフェイスのフィールドには、public static finalがついていることと同じ状態であるためだ(なぜそうなっているか僕はまだ分かっていない)。

inplementedFlidae.java
class Housecat inplements Felidae
{
  void meetsCats()
  {
    System.out.println("猫を発見したよ。多分どこかのイエネコだと思う。");
  }
}

拡張のextendsではなく代わりにimplementsになっているが、形としては今までと変わりない。

原則:スーパークラスはひとつだけ

なぜ抽象メソッド・インターフェイスが必要になるのか。すべてクラス拡張で済ませればいいと思うのだが、Javaは複数のクラスによる多重継承を認めていない。親であるスーパークラスは1つだけに制限されている。人間の考える系統図と同じように考えてはいけないようだ。
その理由を少し見たが、メソッド名がかぶったときの呼び出しとか菱型継承問題(diamond problem)とか、何やらここには深みがあるようだ。なおC++という別言語はこの多重継承を認めている。

しかし、一度用意したものを使えないのは厳しいものがある。そこで、クラスによる多重継承の代わりに、抽象クラスやインターフェイスによる部分的な多重継承のみを許すことで言語としてのシンプルさを保とうとしている。
インターフェイスの実装は2つ以上で行うことが出来る。また、インターフェイス自体を拡張し、サブインターフェイスにスーパーインターフェイスをextendsすることはできる。

終わりに

拡張による継承が許される部分を追ってみると、Javaは演繹的な展開が得意な言語なのかなと感じる。すでに出来上がったクラス・インターフェイスを知り、その機能を継承しながら新しいクラスを組み上げることを想定しているということは、まず標準ライブラリが用意した機能を知るところからJavaの勉強は始まるのだろう。

参考

出来るだけ自分で変数や式を書いてコンパイルしているので、完全に引用する場合はその旨記述する。

やさしいJava 第7版
Java SE11 Silver 問題集(通称黒本)

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

【Java】Apache POIでExcelファイルを扱う

概要

業務でJavaプログラムからExcelファイルを参照、出力(作成)する必要があり、その際に Apache POI を用いて対応したので、その基本的な操作をまとめた。

Apache POI とは?

Apacheプロジェクトの1つで、Word や Excel などの Microsoft Office 形式のファイルを読み書きできる 100% Java ライブラリ。

※ POI という名称は、Office のファイル形式をリバースエンジニアリングした際、その形式が意図的、中途半端に分かりにくくされていたため、皮肉を込め "Poor Obfuscation Implementation"(質の悪い難読な実装)と呼んだものの頭字語に由来している。

Apache POI の入手

Apache POI の公式サイトより、ライブラリを取得する。
(2020年3月時点の最新バージョンは 4.1.2 だが、今回は業務対応時点での最新だった、3.17 にて実装)

サンプルコード

既存のExcelファイルから値を取得し、内容を追記して別のExcelファイルに出力するサンプル。

コード

ApachePoiSample.java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

public class ApachePoiSample {

    /** 入力Excelファイルパス */
    public static final String IN_EXCEL_FILE_PATH = "excel/in/sample.xlsx";
    /** 出力Excelファイルパス */
    public static final String OUT_EXCEL_FILE_PATH = "excel/out/output.xlsx";

    public static void main(String[] args) {

        //---- Excelファイルを開く ----
        Workbook wb = null;
        try ( InputStream is = new FileInputStream(IN_EXCEL_FILE_PATH); ) {
            wb = WorkbookFactory.create(is);
        } catch (FileNotFoundException e) {
            // 対象のExcelファイルが存在しない場合に発生
            e.printStackTrace();
        } catch (IOException e) {
            // 対象のExcelファイルの読み込みに失敗した場合に発生
            e.printStackTrace();
        } catch (EncryptedDocumentException e) {
            // 対象のExcelファイルにパスワードが設定されている場合に発生
            e.printStackTrace();
        } catch (InvalidFormatException e) {
            // 対象のExcelファイルが無効な形式である場合に発生
            e.printStackTrace();
        }

        // 対象のシートを選択する
        Sheet sheet1 = wb.getSheet("Sheet1");

        //---- A列の各セルから値を取得する ----
        System.out.println("<入力>");

        // 文字列を取得する
        Row row = sheet1.getRow(0);     // 1行目
        Cell cell = row.getCell(0);     // A列
        String a1 = cell.getStringCellValue();
        System.out.println("A1 : " + a1);

        // 数値を取得する
        row = sheet1.getRow(1);         // 2行目
        cell = row.getCell(0);          // A列
        double a2 = cell.getNumericCellValue();
        System.out.println("A2 : " + a2);

        // 数式を取得する
        row = sheet1.getRow(2);         // 3行目
        cell = row.getCell(0);          // A列
        String a3 = cell.getCellFormula();
        System.out.println("A3 : " + a3);

        //---- 出力用にB列に値を追記する(B列は空のため、新規にセルを作成する)----
        System.out.println("<出力>");

        // 文字列を出力する
        row = sheet1.getRow(0);         // 1行目
        cell = row.createCell(1);       // B列の作成
        cell.setCellValue(a1 + a1);
        System.out.println("B1 : " + cell.getStringCellValue());

        // 数値を出力する
        row = sheet1.getRow(1);         // 2行目
        cell = row.createCell(1);       // B列の作成
        cell.setCellValue(a2 + a2);
        System.out.println("B2 : " + cell.getNumericCellValue());

        // 数式を出力する
        row = sheet1.getRow(2);         // 3行目
        cell = row.createCell(1);       // B列の作成
        cell.setCellFormula("B2*2");
        System.out.println("A3 : " + cell.getCellFormula());

        //---- Excelファイルに出力する ----
        try ( FileOutputStream os = new FileOutputStream(OUT_EXCEL_FILE_PATH); ) {
            wb.write(os);
        } catch (FileNotFoundException e) {
            // 出力先に指定されたパスが存在しない場合に発生
            e.printStackTrace();
        } catch (IOException e) {
            // Excelファイルの出力に失敗した場合に発生
            e.printStackTrace();
        }
    }

}

実行結果

入力ファイル(sample.xlsx)

A1セルに文字列(Sample)、A2セルに数値(1.23)、A3セルに数式(A2*2)が入力されている。
sample.xlsx

コンソールログ

入力ファイルのA列の各値が取得されている。
また、出力ファイル用の値を以下の様に設定。

<入力>
A1 : sample
A2 : 1.23
A3 : A2*2
<出力>
B1 : samplesample
B2 : 2.46
A3 : B2*2
出力ファイル(output.xlsx)

プログラム中で設定した各値がB列に出力されている。
output.xlsx

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

GithubActionsでMavenプロジェクトのCI

以下の記事の続きで、今回はGithubActionsを使ってみます。
https://qiita.com/kasa_le/items/1ce0d9e34f29e34835cb

Github Actions

Githubの各リポジトリの、[Actions]タブから設定/閲覧出来ます。

githubactions.png

特徴

Github専用のCIツールです。
公式のActionももちろんありますが、それらもまたオープンソースで作っていく、というスタンスが、「らしい」ですよね。
ActionはMarketplaceで探すことが出来ます。
https://github.com/marketplace?type=actions

料金

publicなリポジトリでは利用制限はありません。
privateなリポジトリなどでは、従量課金制となっていますが、毎月無料範囲があります。
見積もり計算方法などは、こちらが参考になるかと思います。
https://qiita.com/euxn23/items/8cec3a3b8de9e3213424

無料利用であっても、Windows/Ubuntu/macOSが選べるのは非常に便利です。

実績

これからという感じですが、なんといってもMicrosoftがついたので、Azure Pipelinesでの知見などもあるでしょうし、今後確実に使われていくと思います。
ただ、逆に、Azure Pipelinesに統合されて消えてしまう可能性もあるかもしれませんよ?:grin:

設定

ymlファイルにて行います。
プロジェクトのルート直下に、.github/wrokflowsというディレクトリを作成し、その配下に置きます。

最初に[Actions]タブをクリックすると、様々なテンプレートから選ぶことが出来、最初のコミットは上記のフォルダに対して行ってくれます。

テンプレートは、Mavenビルドは2つ見つかりますが、今回はビルドだけなので、赤枠の方を使いました。

github_actions_template.png

最終的に目的を達成したymlはこちらになります。

/github/workflows/maven.yml
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven

name: Java CI with Maven

on:
  push:
    branches: [ feature/for_github_actions ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
      uses: actions/setup-java@v1
      with:
        java-version: '11.0.6'
    - name: Build with Maven
      run: mvn clean install -DskipTests
    - name: Test with Maven
      if: success()
      run: |
        mvn test -Dmaven.test.failure.ignore=true site -DgenerateReports=false 
        mvn surefire-report:report -Daggregate=true 
    - name: Archive JAR
      if: success()
      uses: actions/upload-artifact@v1
      with: 
        name: repository
        path: repository/target/repository.jar
    - name: Archive WAR
      if: success()
      uses: actions/upload-artifact@v1
      with: 
        name: serverapi
        path: serverapi/target/serverapi.war
    - name: Archive test resuls
      if: always()
      uses: actions/upload-artifact@v1
      with: 
        name: test-results
        path: target/site

ステップは見たまんまで他のCIツールとも大きく違わないので割愛しますが、ポイントだけ。

  • Set up JDK 11ステップ

    • 公式アクションのsetup-javaを使っています。ただし、こちらだとZulu版のOpenJDKが採用されるようです(Zuluは基本有償ですが、Azureから使うと無償だったかで、その関係でしょうか)。AdoptOpenJDK版を作ってくださっている方もいるので、Marketplaceで探してみて使うのも良いでしょう。
  • Test with Mavenステップ

    • JUnitテストレポートをページ上で見られないので(Workflowの成否でテスト失敗があったかどうかはわかりますが)、xmlをダウンロードしてきても人間の目には分かりづらいため、surefire-reportを使って出したhtml版の方を保存するようにしています。
    • 通常は、以下のように全て一度に指定して、surefireの各モジュールのテスト結果がマージされたレポートが作成されるのですが、それをGithubActions上で実行すると、なぜか結果レポートにはテスト件数が0件になってしまっていました。苦肉の策で、コマンドを分けるとちゃんと生成されるということで、わざわざ2行に分けました。Mavenのバージョンか何かが影響しているんでしょうかね・・・
$ mvn clean \
    test -Dmaven.test.failure.ignore=true \
    site -DgenerateReports=false \
    -Daggregate=true \
    surefire-report:report

感想

Androidビルドでも触っていて(https://qiita.com/kasa_le/items/5feb953cd50489c30c90 )、馴染みがあったのでこれまでの2つ(AppVeyor, CircleCI)よりは設定を楽に出来ました。
surefireのレポートでは少しハマりましたが。

テスト結果をActionsのページ上で見られると楽なのですがね。今はいちいちダウンロードして解凍してからじゃないと見られないので、それが手間です。

Github Pagesにsurefireの吐き出したhtmlを上げることで可能にならないかなあと思っているのですが、まだ詳しく調査はしていません。

そうそう、Artifactのパスが、CircleCI同様、正規表現やワイルドカードが使えないようで、そのへんは不便ですね。

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

CircleCIでMavenプロジェクトのCI

以下の記事の続きで、今回はCircleCIを使ってみます。
https://qiita.com/kasa_le/items/1b97b0d36c81785522a8

CircleCI

こちらから登録できます。
GithubかBitbucketと連携する方法しかないので、それ以外のリポジトリの方は諦めてください(笑)
https://circleci.com/

特徴

テンプレートが豊富だったり、Orbというジョブをまとめたライブラリ的なものを配れたりと、メジャーどころのビルド環境については整っている感じです。.NET、Androidなどもビルドできます。

Githubへの接続は、Authorized OAuth Appsとして行われます。
Githubのアカウントの[Settings]-[Applications]-[Authorized OAuth Apps]に登録されます。(内部的には、SSHキーでの接続のようです。)

キャッシュ機能にも対応しています。

OSイメージはLinux, Windows, macOSとサポートしていますが、Dockerイメージを各種引っ張ってくることができ、今回はこれで助かりました。

料金

フリープランは個人の趣味使用には十分耐えられると思いますが、OSイメージはLinuxかWindowsしか対応していないので要注意です。
それ以外は月額課金となります。

https://circleci.com/ja/pricing/
プラン間の金額差が大きいように見えますが、下の方を見ていくと、チームメンバー数や利用量に応じての課金制(月額)になっています。

CIの稼働が安定的なプロジェクトなら、見積もりやすいと思います。逆に、一定時期だけ保守開発してたくさん回るけど、それ以外の運用中はあまり回らないようなプロジェクトの場合、何もしなくても最低月額はかかるので、そのあたりを見極めて使いたいところですね。

それと、OSSの場合、

毎月 400,000 クレジットを無料プランのお客様に提供します

とのことですが、(https://circleci.com/ja/pricing/ のよくある質問より)Linuxしか使えないようなので、注意が必要です。

また、エンタープライズ向け(要問い合わせ)には、オンプレミス版も提供されています。
https://circleci.com/ja/enterprise/

実績

結構老舗というイメージです。
そのため、現在はVersion2.1ぽいのですが、検索するとまだまだ古い頃の情報が多数引っかかり、今のバージョンでの情報にたどり着くのが少々大変でした。

また、WebサイトのUIを変更中のようで、新しいUIと古いUIがぐちゃぐちゃに遷移するため、「あの画面にはどうやって戻るんだろう?」とかなり迷います。
4月15日以降、古い画面は見られなくなると言っているので、そのあとには落ち着くかもしれません。

ステータスバッジなどをつける際のマークダウンの情報は、古いUIだと自分のプロジェクト、ブランチ名などを設定してコピペすれば良いようなページを提供してくれているのですが、新しいUIではそのページが無くなっていて、汎用的な説明ページでサンプルが載っているだけなので、少し不便になります。

あと、手動でワークフローをを実行することが出来ません。
(実行済みを[Rerun]することは出来ますが)

設定

ymlファイルのみで行います。
プロジェクトのルート直下に、.circleciというディレクトリを作成し、その下にconfig.ymlを配置します。

最終的に、ビルドとテストが上手くいった設定はこちらになります。

circleci/config.yml
version: 2.1

jobs:
  build:
    docker:
      - image: 'cimg/openjdk:11.0.6'
    steps:
      - checkout
      - run: 
          name: Build and test
          command: mvn clean install 
      - store_artifacts:
          path: repository/target/repository.jar
      - store_artifacts:
          path: serverapi/target/serverapi.war
      - run:
          name: Save test results
          command: |
            mkdir -p ~/test-results/junit/
            find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/test-results/junit/ \;
          when: always
      - store_test_results:
          path: ~/test-results
      - store_artifacts:
          path: ~/test-results/junit

workflows:
  version: 2
  maven_test:
    jobs:
      - build:
            filters:
              branches:
                  only: feature/for_circleci

最初のテンプレートでJava(Maven)を選ぶと、Orbを使ったものを作成してくれるのですが、このイメージだとサブモジュールrepositoryjarがないと言われ、それに依存するサブモジュールのビルドが失敗してしまいました。

Orbに対して、ExecutorのctagでJDKのバージョンを指定できそうだったので試行錯誤したのですが、私のymlの理解が足りないせいか、うまく渡せませんでした。

そこで、Orbのソースコードを参考に、上記のようにDockerイメージを使うことで、OpenJDKの11(LTS版)の最新版でビルドできるようになり、成功するようになりました。

Maven Orbのページ
https://circleci.com/orbs/registry/orb/circleci/maven

※もっとも、その後、jarwarのファイル名についてはMavenプロジェクト側のpom.xmlに問題があって直したので、もしかしたらその版だとデフォルトのままで動いたかもしれません。
※そしてそれば、JDKの問題というより、もしかしたらMavenのバージョンの問題かも?

一応ステップを解説すると、次のようになります。

ジョブbuild

  • dockerセクション
    • https://hub.docker.com/r/から落としてくるイメージのパスを指定
  • stepsセクション
    • checkout
      • 対象ブランチをチェックアウト
    • Build and test
      • mvn clean installコマンドを実行
    • store_artifacts
      • repository.jarserverapi.warをアーティファクトとして保存(※)
    • Save test results
      • 各モジュールのsurefireのテストレポートを一つのディレクトリにコピー(※)
    • store_test_results
      • テスト結果をアーカイブ
    • store_artifacts
      • テスト結果をアーティファクトとして保存

ワークフロー

buildジョブに対し、ブランチをfesture/for_circleciに限定するフィルタ設定をしています。

感想

ステップ解説のところの(※)の部分ですが、なんと、ワイルドカードや正規表現によるパス指定に対応してくれていません!

これは非常にやっかいですね・・・

たとえば、Mavenのpomで<build><fileName>タグで生成物のファイル名を指定出来ますが、この指定は任意なんですね。指定が無い場合、デフォルトでは

repository-1.1-SNAPSHOP.jar

みたいな名前がつけられます。バージョンが付くんです。こちらのほうがわかりやすいのですが、これにしていると、ymlのアーティファクトファイル名を、バージョンを上げると後に書き換えなければならず、とても不便です。

なので今回、このプロジェクトでは、<build><fileName>タグでrepository.jarと固定することでその問題を解決するようにしましたが、これはこれで生成物のダウンロード後にバージョンが分かりづらくなり苦労するかもしれません。

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

社内Proxy上でApache Mavenを実行する時の備忘録メモ

社内Proxyが有る環境でApache Maven(以下Maven)を動作させるのに苦労したのでProxy設定の方法を備忘録メモとしてまとめたいと思います。

Apache Mavenとは?

Javaにおけるライブラリの管理、プロジェクトのビルド、テストの実行、デプロイ等を担うビルドツールです。C言語で言うところのGNU Makeに相当する物だと筆者は理解しています。

検証環境

Ubuntu 16.04.5 LTS

Apache Mavenのインストール

Mavenをインストールします。今回の検証はUbuntuで実施しています。

sudo apt install -y maven

インストールが無事完了しているかの確認
$ mvn -v
Apache Maven 3.3.9
Maven home: /usr/share/maven
Java version: 9-internal, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-9-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "4.4.0-137-generic", arch: "amd64", family: "unix"

Proxyの設定

社内Proxyの壁が立ちはだかっている場合/etc/maven/settings.xmlを編集

sudo vim /etc/maven/settings.xml

記載例(必要部分を抜粋)
<!-- proxies
 | This is a list of proxies which can be used on this machine to connect to the network.
 | Unless otherwise specified (by system property or command-line switch), the first proxy | specification in this list marked as active will be used.
 |-->
<proxies>
  <!-- proxy
   | Specification for one proxy, to be used in connecting to the network.
   |-->
  <proxy>
    <id>http_proxy</id>
    <active>true</active>
    <protocol>http</protocol>
    <username>USER_NAME</username>
    <password>PASSWD</password>
    <host>HOST</host>
    <port>PORT</port>
    <nonProxyHosts>NON_PROXY</nonProxyHosts>
  </proxy>
  <proxy>
    <id>https_proxy</id>
    <active>true</active>
    <protocol>https</protocol>
    <username>USER_NAME</username>
    <password>PASSWD</password>
    <host>HOST</host>
    <port>PORT</port>
    <nonProxyHosts>NON_PROXY</nonProxyHosts>
  </proxy>
</proxies>

動作確認

インストールしたMavenの動作確認をテストプロジェクトを作成して行います。Proxy設定が正しく完了出来ていない場合、実行エラーが発生し、Errorと大量に表示されます。

テストプロジェクトの作成

Mavenプロジェクトを動作検証用に作成します。名前はtest-appとします。

mvn archetype:generate

作成例
Choose archetype:
1: internal -> org.apache.maven.archetypes:maven-archetype-archetype (An archetype which contains a sample archetype.)
2: internal -> org.apache.maven.archetypes:maven-archetype-j2ee-simple (An archetype which contains a simplifed sample J2EE application.)
3: internal -> org.apache.maven.archetypes:maven-archetype-plugin (An archetype which contains a sample Maven plugin.)
4: internal -> org.apache.maven.archetypes:maven-archetype-plugin-site (An archetype which contains a sample Maven plugin site.
      This archetype can be layered upon an existing Maven plugin project.)
5: internal -> org.apache.maven.archetypes:maven-archetype-portlet (An archetype which contains a sample JSR-268 Portlet.)
6: internal -> org.apache.maven.archetypes:maven-archetype-profiles ()
7: internal -> org.apache.maven.archetypes:maven-archetype-quickstart (An archetype which contains a sample Maven project.)
8: internal -> org.apache.maven.archetypes:maven-archetype-site (An archetype which contains a sample Maven site which demonstrates
      some of the supported document types like APT, XDoc, and FML and demonstrates how
      to i18n your site. This archetype can be layered upon an existing Maven project.)
9: internal -> org.apache.maven.archetypes:maven-archetype-site-simple (An archetype which contains a sample Maven site.)
10: internal -> org.apache.maven.archetypes:maven-archetype-webapp (An archetype which contains a sample Maven Webapp project.)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 7:【デフォルトのままEnter】
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.1/maven-archetype-quickstart-1.1.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.1/maven-archetype-quickstart-1.1.pom (2 KB at 2.4 
KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-bundles/4/maven-archetype-bundles-4.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-bundles/4/maven-archetype-bundles-4.pom (4 KB at 4.1 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/archetype/maven-archetype/2.0-alpha-5/maven-archetype-2.0-alpha-5.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/archetype/maven-archetype/2.0-alpha-5/maven-archetype-2.0-alpha-5.pom (9 KB at 32.5 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/16/maven-parent-16.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/16/maven-parent-16.pom (23 KB at 45.1 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.1/maven-archetype-quickstart-1.1.jar
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.1/maven-archetype-quickstart-1.1.jar (7 KB at 23.9 KB/sec)
Define value for property 'groupId': com.test
Define value for property 'artifactId': test-app
Define value for property 'version' 1.0-SNAPSHOT: :【デフォルトのままEnter】
Define value for property 'package' com.test: :【デフォルトのままEnter】 
Confirm properties configuration:
groupId: com.test
artifactId: test-app
version: 1.0-SNAPSHOT
package: com.test
 Y: : y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: /home/user
[INFO] Parameter: package, Value: com.test
[INFO] Parameter: groupId, Value: com.test
[INFO] Parameter: artifactId, Value: test-app
[INFO] Parameter: packageName, Value: com.test
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /home/user/test-app
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:15 min
[INFO] Finished at: 2020-03-19T09:24:46+09:00
[INFO] Final Memory: 19M/64M
[INFO] ------------------------------------------------------------------------

参考: 社内でMaven実行時、外部のMavenリポジトリサーバーからダウンロードができない時の対処方法

まとめ

試してみようと思ったOSSがJavaで実装されており、Mavenプロジェクトの状態で配布されていたため、Mavenをインストールしましたが、案の定社内Proxyに阻まれ最初はビルドもままなりませんでした。Proxy絡みの設定はアプリケーションによって異なるのでぶつかった時は解決次第メモとして残しておくことが必要だと思いました。

Reference

Javaビルドツール入門 Maven/Gradle/SBT/Bazel対応 (掌田 津耶乃著; 秀和システム; 2017年)
https://www.codeflow.site/ja/article/maven__how-to-install-maven-in-ubuntu

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

SpringでAOPをテストしたいときはProxyFactoryが便利!

概要

  • AOPの設定が動くようになっているかをテストしたい
  • しかし、中で外部への副作用(例: トランザクション、ログ出力...etc)を及ぼす処理を行っている場合テストがしづらい
  • そんなときはProxyFactoryが便利

まずは普通のテストコードを書いてみる

前提として以下のServiceクラスとMethodInterceptorを用意。

AOPの対象Service
@Service
class SampleService {
    fun execute() {
        println("SampleService#execute")
    }
}
Interceptor
class SampleInterceptor(
        private val name: String
) : MethodInterceptor {
    override fun invoke(invocation: MethodInvocation?): Any? {
        println("intercept by $name")
        return invocation?.proceed()
    }
}

class SampleServicePointCut : StaticMethodMatcherPointcut() {
    override fun matches(method: Method, @Nullable targetClass: Class<*>?): Boolean {
        return targetClass?.let { SampleService::class.java.isAssignableFrom(it) } ?: false
    }
}
config
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
class AopConfig {

    @Bean
    fun interceptorA(): Advisor {
        return DefaultPointcutAdvisor(SampleServicePointCut(), SampleInterceptor("configured interceptor"))
    }
}
テストコード
@SpringBootApplication
class SpringTestApplication

@RunWith(SpringRunner::class)
@SpringBootTest(classes = [SpringTestApplication::class])
internal class SampleServiceTest {
    @Autowired
    private lateinit var service: SampleService

    @Test
    fun test() {
        service.execute()
    }
}
実行結果
intercept by configured interceptor
SampleService#execute

ProxyFactoryを使って以下のようにテストコードを書いてみる

ProxyFactoryを使ったテストコード
@Test
fun testByProxy() {
    val factory = ProxyFactory(SampleService())
    factory.addAdvisor(DefaultPointcutAdvisor(SampleServicePointCut(), SampleInterceptor("Proxy")))
    val proxy = factory.proxy as SampleService
    proxy.execute()
}
実行結果
intercept by Proxy
SampleService#execute

KotlinならUtil関数つくってもいいかも

Extensionを使ってutil関数をつくっておくのもよい。

kotlinのextensionを使った例ProxyFactoryを使ったテストコード
@Test
fun testByProxy() {
    val proxy = SampleService().proxy {
        addAdvisor(DefaultPointcutAdvisor(SampleServicePointCut(), SampleInterceptor("Proxy")))
    }
    proxy.execute()
}

@Suppress("UNCHECKED_CAST")
fun <T : Any> T.proxy(settings: ProxyFactory.() -> Unit): T {
    return ProxyFactory(this).also { settings(it) }.proxy as T
}

@Aspectを使っている場合はAspectJProxyFactory

ServiceとConfig
@Aspect
@Component
class SampleAspect(
        private val name: String = ""
) {
    @Before("execution(* SampleAspectService.*(..))")
    fun advice() {
        println("advice by $name")
    }
}

@Service
class SampleAspectService {
    fun execute() {
        println("SampleAspectService#execute")
    }
}
テストコード
@Test
fun testAspectProxy() {
    val factory = AspectJProxyFactory(SampleAspectService())
    factory.addAspect(SampleAspect("proxy"))
    val proxy = factory.getProxy() as SampleAspectService
    proxy.execute()
}
実行結果
advice by proxy
SampleAspectService#execute
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MyBatis にて値オブジェクト(Value Object)をマッピングさせる際のポイント

前置き

MyBatis にて値オブジェクト(Value Object)をマッピングさせる際に少しハマったので整理しました。

実現させたいこと

  • SELECT の結果をオブジェクト内の Value Object にマッピングさせたい
  • SELECT のパラメーターを Value Object にしたい

環境

  • Spirng Boot
  • MyBatis
  • h2 DataBase

実装

以下のようなUserNameという Value Object クラスがあったとします。

package com.example.demo.domain.model;

public class UserName {
    private final String value;

    public UserName(String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }
}

UserクラスがUserNameを保持します。

他にUserNameRegisterDateというクラスも保持しています。

package com.example.demo.domain.model;

import lombok.Data;

@Data
public class User {
    private UserId userId;
    private UserName userName;
    private RegisterDate registerDate;
}

schema.sqlにDB定義を書きます。

CREATE TABLE users (
  id int NOT NULL
  , user_name VARCHAR(50)  
  , register_date DATE
);

テスト用のデータ挿入用のdata.sqlです。

INSERT INTO users VALUES (1, 'Nocchi', '2020-02-01');
INSERT INTO users VALUES (2, 'Kashiyuka', '2020-02-02');
INSERT INTO users VALUES (3, 'A-Chan', '2020-02-03');

UserRepositoryにIDからUserを取得するためのメソッドfindByIdを定義します。

package com.example.demo.domain.repository;

import com.example.demo.domain.model.User;
import com.example.demo.domain.model.UserId;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface UserRepository {
    User findById(@Param("userId") UserId userId);
}

Mapper は以下のような内容になります。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.repository.UserRepository">
  <select id="findById" resultMap="UserMap" parameterType="map">
    select id, user_name, register_date from users where id = #{userId.value}
  </select>

  <resultMap id="UserMap" type="com.example.demo.domain.model.User">
      <association property="userId" javaType="com.example.demo.domain.model.UserId">
        <constructor>
          <arg name="value" column="id"/>
        </constructor>
      </association>
      <association property="userName" javaType="com.example.demo.domain.model.UserName">
        <constructor>
          <arg name="value" column="user_name"/>
        </constructor>
      </association>
      <association property="registerDate" javaType="com.example.demo.domain.model.RegisterDate">
        <constructor>
          <arg name="value" column="register_date"/>
        </constructor>
      </association>
  </resultMap>

</mapper>

ポイント

  • Value Object を selectの Parameter に使用したい場合@Paramアノテーションをつける
  • @Paramを使用する場合、selectのオプションにparameterType="map"を付与する

上記を行わないと、where id = #{userId.value} のように Value Object の値をselect内で使用できませんでした。

  • SELECT結果のオブジェクト内の Value Object インスタンスを生成するには、associationを使用する
  • Value Object の コンストラクタに値をマップさせるためにconstructorを使用する
  • argのオプションnameで、引数名を指定する

以下の部分です。

<association property="userId" javaType="com.example.demo.domain.model.UserId">
        <constructor>
          <arg name="value" column="id"/>
        </constructor>
</association>

結果

テストコードを書いてブレークさせた結果は以下のように。

Value Object のインスタンスも生成されて、値もマップされています。

スクリーンショット 2020-02-06 5.41.18.png

コード

GitHub に公開しました。

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

Progate無料版をやってみる【Java I】

前回に引き続きProgate無料レッスンをやっていこうと思います。

今回からJavaになります。
業務では2年くらいやりました。まだまだ知識と技術は浅いです。

JavaのレッスンはJava II まで無料のようです。
レッスン一覧

Java I

公式レッスン

環境構築

Eclipseを導入しようと思います。以下の最新にします。
https://mergedoc.osdn.jp/

Tomcat等が入ったFull Editionにしようと思います。
Javaも入ってます。

ダウンロードにめちゃくちゃ時間かかります・・・。

・・・

じかんかかりすぎぃ

1時間以上もかかって、半分もダウンロードが進んでないので
すみませんが、以前ダウンロードしたバージョンのEclipseを使用したいと思います。
たしか2018年くらいの物・・・。

image.png

2019年6月でしたw

・テストプロジェクトを作成します。
image.png
JREは12にしてみました。12はいったい何ができるの・・・。

image.png
え・・・module-info.javaってなに・・・。

参考にさせていただきました。 → モジュールシステムを学ぶ

ちょちょちょちょちょちょちょと逃げさせて・・・。

とりまpublic static void mainを持ったクラスを作らせて。
srcを右クリック→新規追加→クラス
image.png
パッケージ名は適当です。
public static void mainを自動生成する項目にチェックを入れます。
image.png
できました。

あ、Eclipseの外観(テーマ)をC#っぽくしてしまっていたので、ダークテーマに変更します。
image.png

こんな感じ
image.png

ここからレッスンに戻ります。

Javaに触れてみよう

・Javaはバッチ処理の場合、

public static void main

のメソッドが最初に呼ばれる。すべての処理の始まり。

本メソッドはどのクラスにあってもよい。

ただし、実行時に

java クラス名 引数

のクラス名に指定したクラスのpublic static void mainが呼ばれる。

・コマンドラインへの表示処理が

System.out.println("Hello World");

になります。
ブラウザでいうところのconsoleで、Rubyならputs、PHPならecho、C#ならConsole.WriteLineでしょうか・・・。

そういえば今回はJavaなのでコンパイルが必要ですね。
いままでは全部インタープリタ型言語だったので必要ありませんでした。

演習
image.png
これを実行していきます。

Eclipseから実行する場合はpublic static void mainがあるクラスを右クリック → 実行 or デバッグ → Javaアプリケーション
image.png

左下に出てくるウインドウにコンソール結果が出力されました!
image.png

これをコマンドラインからやってみます
Eclipse上で実行すると、コンパイルと実行を自動的にやってくれますが、勉強の為、手動でもやってみたいと思います。

ウィンドウズボタン + Rで以下を立ち上げて
image.png

cmdと打って、コマンドプロンプトを表示させます。
image.png

java -version

と入れて実行して、現在のjavaのバージョンを調べます。
これは環境変数にjavaのパスが設定されていないと表示されません。
EclipseをFull Editionでインストールしていればおそらく勝手に設定されているはず・・・。

私のはバージョン1.8がカレントになっているみたいです。
image.png

Eclipse上ではバージョン12で書いているので、それに合わせたいと思います。

環境変数の画面を開く
(coltanaに入れるか、コントロールパネルからいけます)
image.png
Pathの中身のjavaのところを変更しようと思います。
編集ボタンを押します。
(あれ?JAVA_HOMEはいいんだっけ・・・。)
image.png

バージョン12が配置されている場所を指定します。
Eclipseのインストール先フォルダの中にあります。

変更したら、一度コマンドプロンプトを閉じて、再度立ち上げjava -versionを実行します。
image.png
変わっていればOKです。

実行
コマンドで、Eclipseで作成したファイルのパスに移動します。
cd ~
image.png
image.png

java ProgateTest.java

と実行します。
image.png
表示されました。

ちなみに、Eclipseではutf-8で保存して、コマンドプロンプトはSJIS(MS932)で表示するので、Hello Worldを日本語や2バイト文字にすると文字化けします。
image.png
image.png
これを回避するには、コマンドのオプションでシステムプロパティ-Dを使用します。
参考にさせていただきました。
[Java] システムプロパティのメモ

-Dに続けてfile.encoding=UTF-8記述します。
※Eclipseで作成したファイルのエンコードと合わせます。

java -Dfile.encoding=UTF-8 ProgateTest.java

image.png
文字化けが治りました。

コンパイルして実行
Eclipseで作成したファイルをコンパイルして実行してみようと思います。

javacでコンパイル

javac ProgateTest.java

image.png
文字コードが違う的なエラー。

文字コードを指定してコンパイル。
コンパイルの時はエンコードの指定方法が少し変わる。

javac -encoding UTF8 ProgateTest.java

image.png

クラスファイルが同階層にできます。
image.png

・実行してみます。
コンパイルしたものを実行する場合はクラス名を指定します。

java ProgateTest

image.png
エラーになります。
理由はProgateTestクラスにはパッケージpackage jp.test.testproject;の指定があるためです。
修正して実行してみます。

java jp.test.testproject.ProgateTest

image.png
同じエラーになります。

実行した場所から、パッケージのドットつなぎの構成と一致していないとエラーになります。つまり、jp.test.testproject.ProgateTestは実行している場所からjp/test/testproject/のProgateTest.classを探していることになるので、

/src/jp/test/testproject/jp/test/testproject/ProgateTest.class

があれば、OKということになります。
実際においてみました。
image.png

実行してみます。
image.png
表示されました。

これを踏まえると、先ほどの場合、srcの位置まで戻って、java jp.test.testproject.ProgateTestを実行すればうまくいきます。

ただ、いちいち移動は面倒なので、クラスパスを通してあげます。
このパスを通してあげることにより、そのパスからもjp/test/testproject/のProgateTest.classを探してくれるようになります。

-cpオプションを使用します。

相対パスだと3つ上の階層なので・・・。

java -cp ../../../ jp.test.testproject.ProgateTest

絶対パスだと

java -cp C:/pleiades/workspace/TestProject/src/ jp.test.testproject.ProgateTest

結果
image.png

長くなりましたがレッスンに戻ります。

Javaの基本・文字列

他言語とおおむね似たり寄ったり
・文字列は"で囲む
・命令の最後はセミコロン;
・コメントはスラッシュ2重//

数値、数値の計算

+ - * / %

文字列の連結

・JavaやC#は文字列と数値を足す時は+でよい。
 → 文字列になる
 例: 3 + "2" = "32"になる

変数の定義、変数を使ってみよう、変数の更新

・他言語と同様
 箱。取り出せる。計算できる。

・左辺は今はvarの型推論が主流。

自己代入、自己代入の省略

・他言語と同様

変数の役割と注意点

ここら辺はすべて他言語と同様の内容ですね・・・。

小数を扱おう、自動型変換

・特に・・・

強制型変換

・キャスト
 型変換。

・課題に挑戦しよう
image.png
image.png

クリアしました。
image.png

感想

Full Edition(プレアデス)のインストールがあまりにも時間がかかるのが不便だった。Eclipseだけど・・・。

基礎のみであった。
経験者に無料レッスンは不必要であると感じた。

次回はJava IIをやっていきます。
→ 次回

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