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

JavaでintとIntegerの違い

読者

  • Java覚えたて
  • Android開発者
  • Javaの復習

JavaでintとIntegerの違い

intとIntegerの違いについて掘り下げます。

  • intとは?
    • -2147483648~2147483647の範囲にある整数値を保持するための32ビットの変数型
    • nullは利用不可
    • プリミティブ型
  • Integerとは?
    • –2147483648~2147483647の範囲にある整数値を保持する変数型
    • nullは利用可能
    • オブジェクト型(クラス型)

Integer型はint型のラッパーであり、int型を操作するためのメソッドが用意されている。

利用例

たとえば、int型からString型へキャスト(型変換)したい場合などで利用されます。

int price = 100;

// int -> Integer
Integer priceRap = price;

// Integer -> String
System.out.println("price : " + priceRap.toString());

// Doubleにしたい
Double priceDouble = priceRap.doubleValue();

// Doubleにして税金計算とか?
Double per = 1.1;
System.out.println("Payment amount : " + (priceDouble * per));

なぜ?

なぜ、intとIntegerなどが別れて存在しているのかというと、コンピューターはint型などのプリミティブ型を処理します。

WebAPIのJSONでもプリミティブ型を利用して、オブジェクト型は送信しません。

データを加工したり、保存したり、何かしら操作したい場合にInteger型が必要になってきますので、必要に応じて使い分けてください。

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

GoF javaデザインパターン ざっくり要約

この記事は「Java言語で学ぶ デザインパターン入門」を自分用に要約したものになっています

Iterater

目的

配列などの集合体を順に要素にアクセスする。

内容

hasNext()は次の要素があるかbooleanで返す。次に要素があるかチェックし、next()を実行
next()は次の要素のアクセス

Adopter

目的

API間のズレをなくす

メリット

二つのAPIの間に入りズレをなくす部品を作ることで、再利用性が高くなる。
既存のクラスに調整を加えずに、目的のインターフェース(API)を実装すること

Template Method

目的

スーパークラスで処理の枠組みを決め、サブクラスで具体的な処理を記述する。

メリット

ロジックは共通化することができる。

注意

親クラスは抽象クラス(abstract)で定義。interfaceだと親クラスは処理を書くことはできない。

Factory Method

目的

インスタンス生成を親クラスで定義する(具体的なクラス名は定義しない)。子クラスでは肉付けを行う。

Singleton

目的

たった一つだけしか、インスタンス生成をしない
コンストラクタをprivateにすることで、クラス外でインスタンス生成ができないようにする。
インスタンスのアクセス方法は、getInstance()で唯一のインスタンスを返すだけにする。

注意

private static Singleton singleton = null;
if(singleton == null){
    singleton = new Singleton;
}

という条件式だと複数スレッドで実行したときにマシンの状態によって複数インスタンスが作成されてしまうことがある。非同期にするか、事前に作成しておく。

Prototype

目的

コピーしてインスタンスを作る。

注意

cloneメソッドはshallow copy。インスタンスの中身までをコピーするdeep copyはオーバーライドして実装。
shallow copyはインスタンスのフィールドの内容をそのままコピーするだけ。
cloneメソッドは初期化はしないため別で初期化する必要あり

Builder

目的

全体の構造を親クラスで作り、段階を踏んで複雑な処理を積み上げていく
独立性を高めることで、修正箇所や追加箇所をわかりやすくする。

Abstract Factory

目的

抽象的な工場と抽象的な部品を使用する。

注意

具体的な工場を追加するのは簡単。
新たに部品を追加するのは困難(具体的な工場全体に)

Bridge

目的

機能と実装をわけ、橋渡しをする。
機能・・・処理など
実装・・・実装に使用する場合のクラス

機能を増やすと実装クラスには全て機能が追加されるため、機能を増やしやすくなる。

注意

実装が複雑になってしまうことが多いため、実装の部分を頻繁に変える場合のみ使用する。例えば、OS依存に対する対応は、
機能は修正せずに、実装だけ変えることができる。

Facade

目的

インターフェースを単純化する。
複雑な処理を隠蔽する。複雑な処理の窓口役になる。

注意

packageのみだけしかアクセスできないようにするには、

//public
public class Sample{

}

// package内でしかアクセスできないようにする。
class Sample{

}

Strategy

目的

アルゴリズムを変えることができる。
委譲を使うことで継承より弱い結びつきになり、アルゴリズムを簡単に変えることができる。

Composite

目的

木構造にする。
容器と中身を同一視して、再帰的な構造にする。
composite -> 「混合物」「複合物」

FileDirectory

ファイルディレクトリはファイルとディレクトリがある。
ファイルを順に見ていくとき、サブディレクトリかファイルかは同一視して見ていく。
image.png

DirectoryはEntryに再起的にアクセス可能。
Fileは木構造だとすると、葉
Directoryは枝。

  • HTML
  • メニュー表

Decorator

目的

機能の核から、一皮一皮上から被せて、追加していく。
中身を追加せずに、機能を追加することができる。
委譲を使うことで、緩やかな結合をするため、動的な機能追加ができる。
透過的なインターフェース(API)を保ったまま、オブジェクトを被せて機能を追加していく。

デメリット

小さいクラスが増えてしまう。

(番外編)継承と委譲

継承 -サブクラスは、スーパークラスと同一視することができる-

class Parent {
    ...
    void parentMethod(){
        ...
    }
}
class Child extends Parent {
    ...
    void childMethod(){
        ...
    }
}

Parent obj = new Child();
obj.parentMethod();
Childのインスタンスは、Parent型の変数にそのまま代入できる。Parentから継承しているメソッドを呼び出すことができる。
つまり、
ChildのインスタンスをあたかもParentのインスタンスであるかのように扱っている。
これはサブクラスをスーパークラスと見なす例



逆に
スーパークラスをサブクラスだと見なすにはキャストが必要

Parent obj = new Child();
((Child)obj).childMethod();

委譲 -自分と委譲先を同一視

委譲を使ってインターフェースが透過的になっている場合は、自分と委譲先の同一視をすることが可能

class Rose {
    Violet obj = ...
    void method(){
        obj.method();
    }
}
class Violet {
    void method(){
        ...
    }
}

RoseとVioletは同じメソッドを持っていて、RoseはVioletに委譲している...が共通のメソッドと言うのが明記されていない。

abstract class Flower {
    abstract void method();
}

interface Flower {
    abstract void method();
}

上記のように明記することが可能
場合によって使い分ける。

Visitor

目的

データ構造と処理を分ける。データ構造を訪問するvisitorクラスを作って、そのクラスに処理をしてもらう。

image.png

データ構造を保持している要素に特定の処理をする。
Visitorパターンは、 FileクラスやDirectoryクラスの 部品としての独立性を高めていることになる。 もし、 処理の内容をFileクラスや Directoryクラスのメソッドとしてプログラムしてしまうと、 新しい「処理」を追加して機能拡張したくなるたびに、 FileクラスやDirectoryクラスを修正しなければならなくなる。

その他

LSP(リスコフの置換原則)

親クラスが関係式に対し真であれば、親クラスの派生した小クラスも真である。(置換可能性)

The Open-Closed Principle

特別な理由がない限り将来の拡張を禁止してはいけない。
しかし、拡張を行うたびに既存のクラスが修正しなくてはならないのはだめ。

つまり、既存のクラスを修正せずに拡張できるようにせよ
ということ。

再利用性の高いクラスは閉じられている。
Stringは再利用性が高い、また拡張すると効率が悪くなってしまうため、閉じられている。(finalクラスで定義されているため拡張できない)

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

SpringBoot中Property和Message的加载顺序问题

Spring boot中属性文件加载顺序

  • spring.profiles.include=test1,test2 按照test1,test2的顺序进行加载
  • 无论key定义在哪个文件中,最终生效的总是最后一次加载的key的值

Spring boot 中message文件加载顺序

  • spring.messages.basename=messages1,messages2按照messages1,messages2的顺序进行文件加载
  • 定义在同一个文件中的相同messageId,总是后加载的有效(覆盖)
  • 定义在不同文件中的相同messageId,总是先加载的有效(之前property文件已存在则后续相同key不加载)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】コンパイル時にクラスが見つからない時、どこを見直すべきか

記事の対象読者

Javaコマンドを自分でタカタカして、コンパイルとか実行周りの勉強してみようと思った時、
ソースコード書いて、フォルダ階層作って、ソースコードを配置して、と手順を踏み、
いざ、javacコマンドでコンパイルする段になると

「シンボルを見つけられません」

この1行のせいでいつまでたってもクラスファイルの作成ができないなんてことありませんか。
構文エラーって割と細かく情報が出るので、原因箇所の特定がしやすいのですが
ことクラスが見つからないってエラーに関しては実装内容ごとに修正点は違うので、
「ここをこう直せば直るよ!」というものはなかったりします。

ですが、初学者に真に必要なのは
「どう直すか」という個別ケースにしか使えない情報ではなく、
「仕組み上、ここを見れば原因がわかる」という使い回しのきく
デバッグの知識なんじゃないかなと思います。

この度、コンパイル周りの仕組みを勉強したら「シンボルを見つけられません」に対して
どのようにアプローチすればいいのか、というものがそれなりに見えてきたので、
現時点での私のデバッグ手順に関して、紹介したいなと思います。

視点1:クラス名は合っているか

傷は浅いほうがいい。のちに紹介する観点で壮大な調査を行なった結果、
エラー原因がただのタイピングミスだった日にはつらすぎるので、
まずはTest.javaクラスの宣言がpublic class Tstになってたりしないか(一例です)
タイピングミスを疑います。
クラスの宣言、パッケージの指定以外は、割と構文エラーに引っかかってくるので
私はそこまで深追いしません。

視点2:クラスの種類は何か

ここでの種類とは、見つからないクラスが
ブートストラップクラスなのか、jarファイルに格納されたクラスなのか、
ユーザクラスなのかということです。

ブートストラップクラス、ユーザクラスの詳細について知りたい方は、
Javaのクラスローダやコンパイル時の型の検索について調べてみてください。

ここでは細かな話は抜きにして、ざっくりと紹介します。

  • ブートストラップクラス
    JDKのコアAPIなど、よく使うjava.langパッケージなどの中核機能はこちらに含まれます。
  • ユーザクラス
    ユーザ(プログラマ)が独自に作成するクラスです。ユーザ定義クラスとも言います。
  • jarファイルに格納されたクラス
    サードパーティライブラリ、ユーザが独自に定義したライブラリなどいろいろですが、
    eclipseなんかでよくビルドパスに追加するアレです。

ここで判断できることは何かというと、見つからないクラスが
ブートストラップクラスの場合には「JDKを指定してるJavaの環境変数」を
ユーザクラスの場合には「クラスパス変数」を疑おうぜっていうことです。

ブートストラップクラスなのか判断できん。
という場合は、コアAPIドキュメントなどを参考に。

ブートストラップクラスが見つからない場合

JDKが指定できていない、ということなので、環境変数「JAVA_HOME」が
きちんと設定できているか、見直してみましょう。
JAVA_HOMEの設定方法に関しては記事も多く出ているので割愛します。

jarファイル内のクラスが見つからない場合

jarファイルのクラスパス指定は、クラスファイルのクラスパスの指定とは少し異なります。
とは行っても、指定するのがディレクトリなのか、jarファイルそのものなのかの違いだけです。
コマンドの文法が正しいか、確認してみましょう。

# jarの場合
$ javac -classpath /lib/example-lib.jar Example.java
# クラスファイルの場合
$ javac -classpath /lib Example.java

ユーザクラスが見つからない場合

こっちのケースの場合には見るべき観点が少し増えます。
こちらに該当する場合は、以降の内容を読み進めてください。

視点3:クラスパスは正しく指定されているか

ユーザクラス、サードパーティライブラリの読み込みは、
兎にも角にもクラスパスの指定が正しくなければなりませんし、
クラスパス指定の仕組みをきちんと理解しないとなんとなくでは動いてくれません。

以下、クラスパスを見直す際の観点について紹介していきます。
なお、紹介順通りに調査しなければならないということはありません。

視点3-1:パッケージルートを指定しているか

以外と他の説明に押しやられてさらっと無視されている、
「クラスパスにパッケージの階層を指定するとうまくimportされない」という事実。
というか当たり前すぎて書かれていないんだろうけど、私はここで引っかかりました。

どういうことかというと、以下のようなフォルダ構成があり、UseCommons.javaでは
StrFactoryを使用したいのでcom.example.utilをインポートしているとします。
スクリーンショット 2020-05-25 16.25.32.png

UseCommons.java
package com.example.util;

上記の場合、srcはパッケージルート、com〜appはパッケージ階層という風に認識されます。
これらをコンパイルする際、私が何をしたかというと

# カレントディレクトリは/srcです
$ javac -classpath /src/com/example/util com/example/app/UseCommons.java

上記のように、クラスパスに/src/com/example/utilを指定することで、
util配下のクラスを読み込んで、UseCommons.javaをコンパイルしたかったわけです。
ですが、Javaはクラスパスで指定したディレクトリを起点にパッケージ階層をたどるため、
上記のように設定すると、UseCommons.javaがStrFactory.javaを見に行く際に、
/src/com/example/util/com/example/utilを見に行ってしまうわけです。

つまり、正しいクラスパスはパッケージルートであるsrcを指定した、以下になります。

# カレントディレクトリは/srcです
$ javac -classpath /src com/example/app/UseCommons.java

以上の観点から、-classpathオプションに指定したパッケージルートと
クラスファイルに宣言したimport文の内容を連結させた場所に、
読み込みたいクラスが配置されているかを確認してみましょう。

視点3-2:デフォルト設定が無効になっていないか

これが一番の曲者だと思います。
デフォルトで設定していたものがオプションの指定などで上書きされ、
必要な設定まで無効になっていたりすると、普段は意識していない分、気づきづらいです。

-classpathには通常、「.(カレントディレクトリ)」が設定されています。
それらを踏まえ、以下の例を見てください。
スクリーンショット 2020-05-25 16.16.19.png

最初は、上記のような構成だったため、以下のコマンドで正常にコンパイルできます。

# カレントディレクトリは/srcです
$ javac com/example/app/UseCommons.java

ところがこの後、UseCommons.javaで外部のライブラリ「commons-lang3-3.10.jar」を
使用することになり、フォルダ構成を以下の通り変更しました。
スクリーンショット 2020-05-25 17.00.46.png
そして、commonsのライブラリをクラスパスに追加し、以下のコマンドを実行したところ、
StrFactoryが見つからないというエラーが発生します。

# カレントディレクトリは/srcです
$ javac -classpath /lib/commons-lang3-3.10.jar com/example/app/UseCommons.java

これは、classpathオプションのデフォルト設定である「.(例のカレントディレクトリは/src)」が
上書きされたことで、Javaがsrc配下のクラスを検索できなくなったためです。
よって、上記のコマンドを以下のように修正すると、エラーは解消されます。

# カレントディレクトリは/srcです
$ javac -classpath /lib/commons-lang3-3.10.jar:. com/example/app/UseCommons.java

終わりに

以上が、現在の私が持ちうるコンパイル時のデバッグ観点です。
この他にも様々な見方があると思いますので、あくまでも初心者の方の勉強の
最初の一歩の手助けとして、認識していただければと思います。
観点は広い方がいいので、私自身もさらに学びを深めたいと思います。

また、他の記事でJavaのコンパイルについても書いておりますので、
コンパイルの仕組みについて興味が出たという方は、以下の記事も参照してみてください。

記事へのリンク:Javaコマンド入門 第3章 ソースコードをコンパイルする

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

[Quarkus]GCP Pub/Subサンプルをそのままコピペしても動かない罠

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

Javaのコンソールへの入力

コンソールへの入力

コンソールに値を入力し、その値をプログラム内で使うことです。
コンソールへの入力を受け取るにはScannerというライブラリを使います。
Scannerは「import java.util.Scanner」を使います。
【例】

Main.java
import java.util.Scanner;
class Main {
  public static void main(String[]args) {
  Scanner scanner = new Scanner(System.in);  //Scannerで初期化
  String name = scanner.next();  //文字列の入力の受け取り
  }
}

new Scanner(System.in);で初期化します。
scanner.next();で文字列の入力を受け取ります。

Scannerの使い方

まずScannerを初期化しscannerという変数にいれています。
Scannerではこの初期化したものを代入した変数を使ってメソッドを呼び出します。
scanner.next()で、コンソールに入力された文字列を受け取ります。
【例】

Main.java
import java.util.Scanner;
class Main {
  public static void main (String[] args) {
    Scanner scanner = new Scanner(System.in);
    System.out.print("名前: ");
    String name = scanner.next();
    System.out.println(name + "さん");
  }
}

上記を実行すると「名前:」と出るので、そこで文字列を入力すれば、〇〇さんと出力されます。

数値の入力を受けとり方

先ほどと同様にScannerを使います。
整数を受け取るメソッドはnextIntメソッド、小数を受け取るメソッドはnextDoubleメソッドを使います。
【例】

Main.java
import java.util.Scanner;

class Main {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);

    System.out.print("名前:");
    String firstName = scanner.next();

    System.out.print("名字:");
    String lastName = scanner.next();

    System.out.print("年齢:");
    int age = scanner.nextInt();  //nextIntメソッドで整数を受け取ります

    System.out.print("身長(m):");
    double height = scanner.nextDouble();  //nextDoubleメソッドで少数を受け取ります

    System.out.print("体重(kg):");
    double weight = scanner.nextDouble();

    Person.printData(Person.fullName(firstName, lastName), age, height, weight);
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaはWord文書にテーブルを作成します

Word文書では、表を使用することにより、テキストの内容をより簡潔かつ明確にすることができ、同時にデータの表示をより明確かつ直感的にすることができます。この記事では、Javaコードを使用してWord文書にテーブルを作成し、セルの背景色を設定する方法を紹介します。

使用ツール: Free Spire.Doc for Java(無料版)

JARファイルのインポート方法
方法1: Free Spire.Doc for Javaパッケージをダウンロードして解凍し、Spire.Doc.jarパッケージをlibフォルダーからJavaアプリケーションにインポートします。

方法2: mavenを使用している場合は、pom.xmlファイルに次の依存関係を追加する必要があります。

<repositories>
        <repository>
            <id>com.e-iceblue</id>
            <name>e-iceblue</name>
            <url>http://repo.e-iceblue.com/nexus/content/groups/public/</url>
        </repository>
</repositories>
<dependencies>
    <dependency>
        <groupId>e-iceblue</groupId>
        <artifactId>spire.doc.free</artifactId>
        <version>2.7.3</version>
    </dependency>
</dependencies>

Javaコード例:

import com.spire.doc.*;
import com.spire.doc.documents.*;
import com.spire.doc.fields.TextRange;
import java.awt.*;
public class CreateTable {
    public static void main(String[] args) {
        //Word文書を作成する
        Document document = new Document();
        //sectionを追加
        Section section = document.addSection();

        //表形式のデータ
        String[] header = {"名前", "性別", "部門", "ジョブ番号"};
        String[][] data =
                {
                        new String[]{"Winny", "女性", "会計士", "0109"},
                        new String[]{"Lois", "女性", "販売員", "0111"},
                        new String[]{"Jois", "男性", "技術スタッフ", "0110"},
                        new String[]{"Moon", "女性", "販売員", "0112"},
                        new String[]{"Vinit", "女性", "支援スタッフ", "0113"},
                };

        //テーブルを追加
        Table table = section.addTable(true);

        //テーブルの行と列の数を設定する
        table.resetCells(data.length + 1, header.length);

        //最初の行をテーブルヘッダーとして設定し、データを追加する
        TableRow row = table.getRows().get(0);
        row.isHeader(true);
        row.setHeight(20);
        row.setHeightType(TableRowHeightType.Exactly);
        row.getRowFormat().setBackColor(Color.gray);
        for (int i = 0; i < header.length; i++) {
            row.getCells().get(i).getCellFormat().setVerticalAlignment(VerticalAlignment.Middle);
            Paragraph p = row.getCells().get(i).addParagraph();
            p.getFormat().setHorizontalAlignment(HorizontalAlignment.Center);
            TextRange range1 = p.appendText(header[i]);
            range1.getCharacterFormat().setFontName("Arial");
            range1.getCharacterFormat().setFontSize(12f);
            range1.getCharacterFormat().setBold(true);
        }

        //残りの行にデータを追加する
        for (int r = 0; r < data.length; r++) {
            TableRow dataRow = table.getRows().get(r + 1);
            dataRow.setHeight(25);
            dataRow.setHeightType(TableRowHeightType.Exactly);
            dataRow.getRowFormat().setBackColor(Color.white);
            for (int c = 0; c < data[r].length; c++) {
                dataRow.getCells().get(c).getCellFormat().setVerticalAlignment(VerticalAlignment.Middle);
                TextRange range2 = dataRow.getCells().get(c).addParagraph().appendText(data[r][c]);
                range2.getCharacterFormat().setFontName("Arial");
                range2.getCharacterFormat().setFontSize(10f);
            }
        }

        //セルの背景色を設定する
        for (int j = 1; j < table.getRows().getCount(); j++) {
            if (j % 2 == 0) {
                TableRow row2 = table.getRows().get(j);
                for (int f = 0; f < row2.getCells().getCount(); f++) {
                    row2.getCells().get(f).getCellFormat().setBackColor(new Color(173, 216, 230));
                }
            }
        }

        //ドキュメントを保存します
        document.saveToFile("Table.docx", FileFormat.Docx_2013);
    }
}

出力ドキュメント:
table.jpg

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

Macでインポートする際、Gradleビルドが止まってしまう時

発生している問題・エラーメッセージ

表題の通りSTSでSpringbootを使用したアプリケーションの作成時にインポートをする際、
ビルド中に固まってしまい、STSが完全に止まってしまった。
エラーメッセージを見てみると

org.gradle.tooling.BuildException: Gradle distribution 'https://services.gradle.org/distributions/gradle-5.4.1-bin.zip' を使用してビルド・アクションを実行できませんでした。

が出てくる。

結論

https://stackoverrun.com/ja/q/11329562
スクリーンショット 2020-05-21 16.01.59.png

こちらの回答にある、ユーザーディレクトリの隠しフォルダにある.gradle/caches/5.4.1にある
「scripts」および「scripts-remapped」フォルダを移動し、STSを再起動→インポートでビルドが出来た。

いろいろと試したこと

・Gradleのバージョンが原因だと思い、最新バージョンでビルド。
→STSが固まった。

・Eclipseのメモリが原因?
https://qiita.com/crarrry/items/7601290c11f7a310913b
こちらを参考に、メモリを上げてみた。
→STSが固まった。

・毎回同じところで固まるので、インポートの途中で変にファイルが作成されていることが原因と特定。
https://stackoverrun.com/ja/q/11329562

原因

インポート中に予期せぬ動作終了が起きてしまい、Gradleビルド中に中途半端なファイルが作成されてしまった。

最後に

環境構築に時間を取られがちです。
STSよりもAndroid studioでこういったエラーの情報が多かったので、ご参考になればと思います。

補足情報(FW/ツールのバージョンなど)

Mac Catalina バージョン10.15.4
JDK 14.0.1
STS 4.6.1

参考
https://qiita.com/crarrry/items/7601290c11f7a310913b
https://stackoverrun.com/ja/q/11329562

共に働くWebエンジニアを募集しています!

不動産SHOPナカジツでは自社サービスを作っていく仲間を募集しています。
詳しくはWantedlyからお問い合わせください。

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

DaprでRubyとJavaを連携させてみる

はじめに

マイクロサービス開発を容易にするDaprをJavaで利用してみたに引き続きDaprです。

チュートリアルのStep6に相当します。
コードは下記を参照
https://github.com/koduki/example-dapr/tree/v01

構成概要

チュートリアルではPythonですが、趣味の問題でRubyを使っています。
この構成のポイントはRubyとJavaは直接通信しないどころはRubyはあくまで自分のサイドカーのDaprと話すのであってJavaを起動しているDaprとは直接話さないことです。

dapr_sample01.png

テストがローカルなので実は直接話すこともできるのですが、分かりやすくJavaのDaprポートを3500, RubyのDaprポートを3600とします。

コード

今回は1秒に一度、対象APIにリクエストを投げるだけの超シンプルなコードです。
javaappはJavaアプリのappidです。こちらを使ってDparどうして適切なアプリを自動で発見します。APIが動いているIPとかポート番号の指定は不要です。
Rubyコード内で指定しているhttp://localhost:#{MY_DAPR_PORT}はJava側のDaprではなくRuby側のDaprです。なので常にlocalhostですし、ポート番号も今回で言えば3500では無く3600です。

require 'net/https'
require "json"

MY_DAPR_PORT=ARGV[0]

n = 0
while true do
    n += 1
    params = {data: {orderId: n}}

    url = "http://localhost:#{MY_DAPR_PORT}/v1.0/invoke/javaapp/method/neworder"
    uri = URI.parse(url)
    http = Net::HTTP.new(uri.host, uri.port)

    headers = { "Content-Type" => "application/json" }
    response = http.post(uri.path, params.to_json, headers)

    sleep 1
end

実行&確認

では実行してみましょう。DaprはWebアプリだけでは無くコマンドも問題なくラッピングできるようです。
まず、API側を立ち上げます。

$ dapr run --app-id javaapp --app-port 8080 --port 3500 ./mvnw quarkus:dev

続いて別ターミナルでクライアント側を立ち上げます。

$ dapr run --app-id rubyapp --port 3600 ruby app.rb 3600

Java側の実行結果を見ると以下のようになっています。

== APP == orderId: 1
== APP == orderId: 2
== APP == orderId: 3

ちゃんと毎秒定期実行されてるのが分かりますね。

dapr listを実行することで今起動しているDaprの一覧も確認できます。

$dapr list
  APP ID   HTTP PORT  GRPC PORT  APP PORT  COMMAND             AGE  CREATED              PID    
  rubyapp  3600       55932      0         ruby app.rb 3600    28m  2020-05-24 21:08.13  44897  
  javaapp  3500       56777      8080      ./mvnw quarkus:dev  3d   2020-05-21 21:21.30  27471  

まとめ

上手くRubyとJavaでDaprを経由して通信ができました。

このアーキテクチャの面白いところはRubyコードもJavaコードもあくまでDaprとしか会話してないのでその間はHTTPなりgRPCなりでセキュリティをさほど気にしなくて良いことです。
通信の暗号化とか認証とかはDapr同士がやれば問題ないのでセキュリティに関する部分を外だしできるのは大きいですね。

分散トレーシングとかTwitter APIやKafkaなど他のコネクタもあるはずなのでそこら辺も試してみたいと思います。
同じマイクロサービス支援ということでKNativeと似たレイヤーかと最初は思ってたのですが、あちらはビルド環境やFaaS, Servelessと言ったインフラ観点よりなのに対し、こちらはかなりアプリより仕組みなのでCNCFにも採択されてますし今後が気になるツールなのは間違い無いですね。

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

StringBuilderメモ

可変な文字列であるStringBuilder。文字列を高速に連結するのに使うらしいです。
今回もStringBuilderのメソッドを使用して、使い方を学んでいきます。

append

appendを使って文字列の追加処理が可能です。
boolean型、char型、String型、配列などいろんな型を文字列として追加することができます。

StringBuilder sb = new StringBuilder("おはよう");

//boolean
boolean bool = true;
System.out.println(sb.append(bool));

//char
char c = 'A';
System.out.println(sb.append(c));


StringBuilder sbr = new StringBuilder("Aさん");

//配列
String[] str = {"こんにちは","おやすみ"};
System.out.println(sbr.append(str[0]));
System.out.println(sbr.append(str[1]));

StringBuilder s = new StringBuilder("Aさん");

//int,double,float,long
int i = 100;
double d = 100;
float f = 100;
long l = 100;
System.out.println(s.append(i));
System.out.println(s.append(d));
System.out.println(s.append(f));
System.out.println(s.append(l));
//boolean
おはようtrue
//char
おはようtrueA
//配列
Aさんこんにちは
Aさんこんにちはおやすみ
//int
Aさん100
//double
Aさん100100.0
//float
Aさん100100.0100.0
//long
Aさん100100.0100.0100

charAt(int index)

このシーケンス内の指定されたインデックスのchar値を返します。

StringBuilder sb = new StringBuilder("ABCDs");

System.out.println(sb.charAt(0));
System.out.println(sb.charAt(2));
System.out.println(sb.charAt(4));
A
C
s

delete(int start, int end)

このシーケンスの部分文字列内の文字を削除します。
start - 開始インデックス(この値を含む)。
end - 終了インデックス(この値を含まない)。

StringBuilder sb = new StringBuilder("ABCDs");

sb.delete(1, 3);
System.out.println(sb);
ADs

deleteCharAt(int index)

このシーケンス内の指定された位置にあるcharを削除します。

StringBuilder sb = new StringBuilder("ABCDs");

sb.deleteCharAt(2);
System.out.println(sb.toString());
ABDs

indexOf(String str)

この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。

StringBuilder sb = new StringBuilder("ABCDs");

System.out.println(sb.indexOf("C"));
System.out.println(sb.indexOf("s"));
2
4

indexOf(String str, int fromIndex)

指定されたインデックス以降で、指定された部分文字列がこの文字列内で最初に出現する位置のインデックスを返します。

StringBuilder sb = new StringBuilder("ABCDsD");

System.out.println(sb.indexOf("D",2));
System.out.println(sb.indexOf("s",0));
3
4

lastIndexOf(String str)

この文字列内で、指定された部分文字列が一番右に出現する位置のインデックスを返します。

StringBuilder sb = new StringBuilder("ABCDssD");

System.out.println(sb.lastIndexOf("D"));
System.out.println(sb.lastIndexOf("s"));
6
5

length()

長さ(文字数)を返します。

StringBuilder sb = new StringBuilder("ABCDssD");

System.out.println(sb.length());
7

replace(int start, int end, String str)

このシーケンスの部分文字列内の文字を、指定されたString内の文字で置き換えます。

StringBuilder sb = new StringBuilder("ABCDssD");

System.out.println(sb.replace(2, 5, "r"));
ABrsD

reverse()

この文字シーケンスを、シーケンスの順序を逆にしたもので置き換えます。

StringBuilder sb = new StringBuilder("ABCDssD");

System.out.println(sb.reverse());
DssDCBA

setCharAt(int index, char ch)

指定されたインデックスの文字がchに設定されます。

StringBuilder sb = new StringBuilder("ABCDssD");

sb.setCharAt(1, 's');
System.out.println(sb.toString());
AsCDssD

setLength(int newLength)

文字シーケンスの長さを設定します。

StringBuilder sb = new StringBuilder("ABCDssD");

System.out.println(sb.length());
sb.setLength(20);
System.out.println(sb.length());
7
20

subSequence(int start, int end)

このシーケンスのサブシーケンスである新規文字シーケンスを返します。
start - 開始インデックス(この値を含む)。
end - 終了インデックス(この値を含まない)。

StringBuilder sb = new StringBuilder("ABCDssD");

System.out.println(sb.subSequence(0, 2));
System.out.println(sb.subSequence(4, 5));
AB
s

substring(int start), substring(int start, int end)

この文字シーケンスに現在含まれている文字の部分シーケンスを含む新しいStringを返します。

StringBuilder sb = new StringBuilder("ABCDssD");

System.out.println(sb.substring(2));
System.out.println(sb.substring(5));

System.out.println(sb.substring(5,6));
System.out.println(sb.substring(0,2));
CDssD
sD

s
AB

insert

指定インデックスに指定した値を挿入する

StringBuilder sb = new StringBuilder("ABCDssD");

//0番目に'P'を挿入する
sb.insert(0, 'P');
System.out.println(sb.toString());
//8番目に23を挿入する
sb.insert(8, 23);
System.out.println(sb.toString());
int[] i = {10,20,30,40};
//0番目にi配列のインデックス2を挿入する
sb.insert(0, i[2]);
System.out.println(sb.toString());
PABCDssD
PABCDssD23
30PABCDssD23

capacity()

現在の容量を返します。

StringBuilder s = new StringBuilder();
//初期容量が16文字
System.out.println(s.capacity());
//初期容量16 + 追加14文字で現在の容量が30文字
StringBuilder sb = new StringBuilder("ABCDssDAAAAAAA");
System.out.println(sb.capacity());
16
30

trimToSize()

この文字シーケンスで使用されているストレージの低減を試みます。

StringBuilder sb = new StringBuilder("A");
//初期容量16 + 追加1文字で現在の容量が17文字
System.out.println(sb.capacity());
//初期容量分の16文字が削除され、1文字
sb.trimToSize();
System.out.println(sb.capacity());

StringBuilder s = new StringBuilder("AAAAAAAAAA");
//初期容量16 + 追加10文字で現在の容量が26文字
System.out.println(s.capacity());
//初期容量分の16文字が削除され、10文字
s.trimToSize();
System.out.println(s.capacity());
17
1

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

[Spring Boot] ページング処理簡単レシピ

環境

言語 : java 1.8
フレームワーク : spring boot 2.2.4.RELEASE (spring-boot-starter)
テンプレートエンジン : thymeleaf 2.2.4.RELEASE (spring-boot-starter)
データベース : H2 1.4.200 (spring-boot-starter)
orm : data-jpa 2.2.4.RELEASE (spring-boot-starter)

ソースコード

Entity

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

@Entity
public class Board {

    @Id @GeneratedValue
    private String id;

    private String title;

    private String user; 

    // Getter・Setter省略

}

Pagination

public class Pagination {

    /** 1. ページことに表示する掲示物数 **/
    private int pageSize = 10;

    /** 2. ページングした、ブロック数 **/
    private int blockSize = 10;

    /** 3. 現在ページ **/
    private int page = 1;

    /** 4. 現在ブロック **/
    private int block = 1;

    /** 5. 総掲示物数 **/
    private int totalListCnt;

    /** 6. 総ページ数 **/
    private int totalPageCnt;

    /** 7. 総ブロック数 **/
    private int totalBlockCnt;

    /** 8. ブロックスタートページ **/
    private int startPage = 1;

    /** 9. ブロック最後ページ **/
    private int endPage = 1;

    /** 10. DB接近スタートインデックス **/
    private int startIndex = 0;

    /** 11. 以前ブロックの最後ページ **/
    private int prevBlock;

    /** 12. 次のブロックの最後ページ **/
    private int nextBlock;

    // Getter・Setter省略

    public Pagination(int totalListCnt, int page) {

        // 総掲示物数と現在ページはコントローラーからもらう。
        // 総掲示物数  - totalListCnt
        // 現在ページ  - page

        /** 3. 現在ページ **/
        setPage(page);

        /** 5. 総掲示物数 **/
        setTotalListCnt(totalListCnt);

        /** 6. 総ページ数 **/
        setTotalPageCnt((int) Math.ceil(totalListCnt * 1.0 / pageSize));

        /** 7. 総ブロック数 **/
        setTotalBlockCnt((int) Math.ceil(totalPageCnt * 1.0 / blockSize));

        /** 4. 現在ブロック **/
        setBlock((int) Math.ceil((page * 1.0)/blockSize)); 

        /** 8. ブロックスタートページ **/
        setStartPage((block - 1) * blockSize + 1);

        /** 9. ブロック最後ページ **/
        setEndPage(startPage + blockSize - 1);

        /* === ブラック最後ページについてバリエーション ===*/
        if(endPage > totalPageCnt){this.endPage = totalPageCnt;}

        /** 11. 以前ブロックの最後ページ **/
        setPrevBlock((block * blockSize) - blockSize);

        /* === 以前ブロックについてバリエーション === */
        if(prevBlock < 1) {this.prevBlock = 1;}

        /** 12. 次のブロックの最後ページ **/
        setNextBlock((block * blockSize) + 1);

        /* === 次のブロックについてバリエーション ===*/
        if(nextBlock > totalPageCnt) {nextBlock = totalPageCnt;}

        /** 10. DB接近スタートインデックス **/
        setStartIndex((page-1) * pageSize);

    }
}

Controller

@GetMapping("/")
public String home(Model model, @RequestParam(defaultValue = "1") int page) {

    // 総掲示物数 
    int totalListCnt = boardRepository.findAllCnt();

    // 総掲示物数と現在ページ
    Pagination pagination = new Pagination(totalListCnt, page);

    // DB接近スタートインデックス
    int startIndex = pagination.getStartIndex();

    // ページことに表示する掲示物最大数
    int pageSize = pagination.getPageSize();

    // 掲示物取得
    List<Board> boardList = boardRepository.findListPaging(startIndex, pageSize);

    // モデルオブジェクトにオブジェクト格納
    model.addAttribute("boardList", boardList);
    model.addAttribute("pagination", pagination);

    return "index";
}

Repository

@Repository
public class BoardRepository {

    @PersistenceContext
    private EntityManager em;

    public int findAllCnt() {
        return ((Number) em.createQuery("select count(*) from Board")
                    .getSingleResult()).intValue();
    }

    public List<Board> findListPaging(int startIndex, int pageSize) {
        return em.createQuery("select b from Board b", Board.class)
                    .setFirstResult(startIndex)
                    .setMaxResults(pageSize)
                    .getResultList();
    }
}

html

<!DOCTYPE html>
<html   lang="ja" 
        xmlns="http://www.w3.org/1999/xhtml" 
        xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <title>paging</title>
        <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
    </head>
<body>

<table class="table table-striped">

  <thead class="thead-dark">
    <tr>
      <th scope="col" style="width: 10%">no</th>
      <th scope="col">title</th>
      <th scope="col" style="width: 15%">user</th>
    </tr>
  </thead>

  <tbody>
    <tr th:each="board : ${boardList}">
      <th scope="row" th:text="${boardStat.index + 1}">1</th>
      <td th:text="${board.title}"></td>
      <td th:text="${board.user}"></td>
    </tr>
  </tbody>

</table>

// ページング
<nav aria-label="Page navigation example ">
  <ul class="pagination">
  <li class="page-item">
      <a class="page-link" th:href="@{/?page=1}" aria-label="Previous">
        <span aria-hidden="true"><<</span>
      </a>
    </li>
    <li class="page-item">
      <a class="page-link" th:href="@{/?page={page} (page = ${pagination.prevBlock})}" aria-label="Previous">
        <span aria-hidden="true"></span>
      </a>
    </li>
    <th:block  th:with="start = ${pagination.startPage}, end = ${pagination.endPage}">
        <li class="page-item" 
                 th:with="start = ${pagination.startPage}, end = ${pagination.endPage}"
                th:each="pageButton : ${#numbers.sequence(start, end)}">
                <a class="page-link" th:href="@{/?page={page} (page = ${pageButton})}" th:text=${pageButton}></a>
        </li>
    </th:block>
    <li class="page-item">
      <a class="page-link" th:href="@{?page={page} (page = ${pagination.nextBlock})}" aria-label="Next">
        <span aria-hidden="true"></span>
      </a>
    </li>
    <li class="page-item">
      <a class="page-link" th:href="@{?page={page} (page = ${pagination.totalPageCnt})}" aria-label="Previous">
        <span aria-hidden="true">>></span>
      </a>
    </li>
  </ul>
</nav>

</body>
</html>

備考

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

Ruby Python Java 大文字小文字を区別しないソート

はじめに

ソートと言えば、数値順・辞書順ですが、それ以外のソートについて調べてみました。

嵌った話

@jag_507 さんのAtCoderでRuby学習10【第一回アルゴリズム実技検定 DoubleCamelCase Sort】を読んで、AtCoder 第一回アルゴリズム実技検定 F - DoubleCamelCase Sortをやってみましたが、ソートがうまくいかないです。

sort.rb
a = ["FisH", "DoG", "CaT", "AA", "AaA", "AbC", "AC"]
a.sort
 # => ["AA", "AC", "AaA", "AbC", "CaT", "DoG", "FisH"] # 実際の返り値
      ["AA", "AaA", "AbC", "AC", "CaT", "DoG", "FisH"] # 期待する返り値

この記事:sortコマンド、基本と応用とワナの様な-fオプションはないのか、ドラえもんGoogle先生助けてー。

大文字小文字を区別しないソート

Ruby に限らず多くのプログラミング言語の辞書順のソートはASCIIコード順での並び替えになりますので、大文字と小文字では大文字が若くなります。
そこで、大文字小文字を区別しないソートが必要になります。

Ruby

ruby.rb
a = ['a', 'b', 'c', 'd', 'e', 'A', 'B', 'C', 'D', 'E']
p a.sort
 # => ["A", "B", "C", "D", "E", "a", "b", "c", "d", "e"]
p a
 # => ["a", "b", "c", "d", "e", "A", "B", "C", "D", "E"]
p a.sort{|x, y| x.casecmp(y).nonzero? || x <=> y}
 # => ["A", "a", "B", "b", "C", "c", "D", "d", "E", "e"]
p a.sort_by{ |s| [s.downcase, s] }
 # => ["A", "a", "B", "b", "C", "c", "D", "d", "E", "e"]

casecomp が大文字小文字を区別しないメソッドになります。
追記
コメント欄よりsort_byの解法をいただきました。
最優先s.downcaseと次優先sの2つ条件でソートを行っています。

Python

python.py
a = ['a', 'b', 'c', 'd', 'e', 'A', 'B', 'C', 'D', 'E']
print(sorted(a))
 # => ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']
print(a)
 # => ['a', 'b', 'c', 'd', 'e', 'A', 'B', 'C', 'D', 'E']
print(sorted(sorted(a), key=str.lower))
 # => ['A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e']

ソート HOW TO に、大文字小文字を区別しない文字列比較の例が載っています。
Python のソートは安定なことが保証されていますので、2重にソートする方法が考えられます。

Java

java.java
        List<String> a = Arrays.asList("a", "b", "c", "d", "e", "A", "B", "C", "D", "E");
        a.sort(Comparator.naturalOrder());
        System.out.println(a); // [A, B, C, D, E, a, b, c, d, e]
        a.sort(String.CASE_INSENSITIVE_ORDER);
        System.out.println(a); // [A, a, B, b, C, c, D, d, E, e]

Java の場合、CASE_INSENSITIVE_ORDERが準備されています。

まとめ

  • 大文字小文字を区別しないソート に詳しくなった
  • Ruby に詳しくなった
  • Python に詳しくなった
  • Java に詳しくなった
  • 自然順のソートまで手が回らなかった

参照したサイト
AtCoderでRuby学習10【第一回アルゴリズム実技検定 DoubleCamelCase Sort】
sortコマンド、基本と応用とワナ
casecomp
ソート HOW TO
[Java 8]コーディングテストで使えるアルファベット順、文字列長順のソート方法

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

OpenTracing チュートリアル 後半

OpenTracing チュートリアル 後半

上記の記事からの続きになります。

Lesson 3のソースをビルドして実行してみる

README.md を見つつ、ソースを編集

src/main/java/lesson03/exercise/Hello.java
package lesson03.exercise;

import java.io.IOException;

import com.google.common.collect.ImmutableMap;

import io.jaegertracing.internal.JaegerTracer;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import io.opentracing.tag.Tags;
import lib.Tracing;

import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class Hello {

    private final Tracer tracer;
    private final OkHttpClient client;

    private Hello(Tracer tracer) {
        this.tracer = tracer;
        this.client = new OkHttpClient();
    }

    private String getHttp(int port, String path, String param, String value) {
        try {
            HttpUrl url = new HttpUrl.Builder().scheme("http").host("localhost").port(port).addPathSegment(path)
                    .addQueryParameter(param, value).build();
            Request.Builder requestBuilder = new Request.Builder().url(url);

            Tags.SPAN_KIND.set(tracer.activeSpan(), Tags.SPAN_KIND_CLIENT);
            Tags.HTTP_METHOD.set(tracer.activeSpan(), "GET");
            Tags.HTTP_URL.set(tracer.activeSpan(), url.toString());
            tracer.inject(tracer.activeSpan().context(), Format.Builtin.HTTP_HEADERS, new RequestBuilderCarrier(requestBuilder));

            Request request = requestBuilder.build();
            Response response = client.newCall(request).execute();
            if (response.code() != 200) {
                throw new RuntimeException("Bad HTTP result: " + response);
            }
            return response.body().string();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void sayHello(String helloTo) {
        // Operation: tracer.buildSpan("operationName");
        // Generating trace information: tracer.buildSpan();
        // Trace start: .start();
        Span span = tracer.buildSpan("say-hello").start();
        try (Scope scope = tracer.scopeManager().activate(span)) {
            // Set span tags: span.setTag("key", value);
            span.setTag("hello-to", helloTo);

            String helloStr = formatString(helloTo);
            printHello(helloStr);
        } finally {
            span.finish(); // Trace end: span.finish
        }
    }

    private String formatString(String helloTo) {
        // Operation: tracer.buildSpan("operationName");
        // Generating trace information: tracer.buildSpan();
        // Trace start: .start();
        Span span = tracer.buildSpan("formatString").start();
        try (Scope scope = tracer.scopeManager().activate(span)) {
            // In order to pass its context over the HTTP request we need to call tracer.
            // inject before building the HTTP request in Hello#getHttp()
            String helloStr = getHttp(8081, "format", "helloTo", helloTo);
            // Set span logs: span.log(ImmutableMap.of("key1", "value1", "key2", "value2"));
            span.log(ImmutableMap.of("event", "string-format", "value", helloStr));
            return helloStr;
        } finally {
            span.finish(); // Trace end: span.finish

        }
    }

    private void printHello(String helloStr) {
        // Operation: tracer.buildSpan("operationName");
        // Generating trace information: tracer.buildSpan();
        // Trace start: .start();
        Span span = tracer.buildSpan("printHello").start();
        try (Scope scope = tracer.scopeManager().activate(span)) {
            // In order to pass its context over the HTTP request we need to call tracer.
            // inject before building the HTTP request in Hello#getHttp()
            getHttp(8082, "publish", "helloStr", helloStr);
             // Set span logs: span.log(ImmutableMap.of("key1", "value1"));
            span.log(ImmutableMap.of("event", "println"));
        } finally {
            span.finish(); // Trace end: span.finish
        }
    }

    public static void main(String[] args) {
        if (args.length != 1) {
            throw new IllegalArgumentException("Expecting one argument");
        }
        String helloTo = args[0];
        // Service: Tracing.init("serviceName");
        try (JaegerTracer tracer = Tracing.init("hello-world")) {
            new Hello(tracer).sayHello(helloTo);
        }
    }
}
src/main/java/lesson03/exercise/Formatter.java
package lesson03.exercise;

import com.google.common.collect.ImmutableMap;
import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.setup.Environment;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
import lib.Tracing;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;

public class Formatter extends Application<Configuration> {
    private final Tracer tracer;

    private Formatter(Tracer tracer) {
        this.tracer = tracer;
    }

    @Path("/format")
    @Produces(MediaType.TEXT_PLAIN)
    public class FormatterResource {

        @GET
        public String format(@QueryParam("helloTo") String helloTo, @Context HttpHeaders httpHeaders) {
            // Operation: Tracing.startServerSpan(tracer, httpHeaders, "operationName");
            Span span = Tracing.startServerSpan(tracer, httpHeaders, "format");
            try (Scope scope = tracer.scopeManager().activate(span)) {
                String helloStr = String.format("Hello, %s!", helloTo);
                // Set span logs: span.log(ImmutableMap.of("key1", "value1", "key2", "value2"));
                span.log(ImmutableMap.of("event", "string-format", "value", helloStr));
                return helloStr;
            } finally {
                span.finish(); // Trace end: span.finish();
            }
        }

    }

    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        environment.jersey().register(new FormatterResource());
    }

    public static void main(String[] args) throws Exception {
        System.setProperty("dw.server.applicationConnectors[0].port", "8081");
        System.setProperty("dw.server.adminConnectors[0].port", "9081");
        // Service: Tracing.init("serviceName");
        new Formatter(Tracing.init("formatter")).run(args);
    }
}
src/main/java/lesson03/exercise/Publisher.java
package lesson03.exercise;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;

import com.google.common.collect.ImmutableMap;

import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.setup.Environment;
import io.jaegertracing.internal.JaegerTracer;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
import lib.Tracing;

public class Publisher extends Application<Configuration> {

    private final Tracer tracer;

    private Publisher(Tracer tracer) {
        this.tracer = tracer;
    }

    @Path("/publish")
    @Produces(MediaType.TEXT_PLAIN)
    public class PublisherResource {

        @GET
        public String format(@QueryParam("helloStr") String helloStr, @Context HttpHeaders httpHeaders) {
            // Operation: Tracing.startServerSpan(tracer, httpHeaders, "operationName");
            Span span = Tracing.startServerSpan(tracer, httpHeaders, "publish");
            try (Scope scope = tracer.scopeManager().activate(span)) {
                System.out.println(helloStr);
                // Set span logs: span.log(ImmutableMap.of("key1", "value1", "key2", "value2"));
                span.log(ImmutableMap.of("event", "println", "value", helloStr));
                return "published";
            } finally {
                span.finish(); //Trace end: span.finish();
            }
        }
    }

    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        environment.jersey().register(new PublisherResource());
    }

    public static void main(String[] args) throws Exception {
        System.setProperty("dw.server.applicationConnectors[0].port", "8082");
        System.setProperty("dw.server.adminConnectors[0].port", "9082");
        // Service: Tracing.init("serviceName");
        new Publisher(Tracing.init("publisher")).run(args);
    }
}

ビルド

[root@localhost java]# mvn clean package

実行

Lesson3.log
[root@localhost java]# ./run.sh lesson03.exercise.Formatter server
[root@localhost java]# ./run.sh lesson03.exercise.Publisher server
[root@localhost java]# curl 'http://localhost:8081/format?helloTo=Bryan'
Hello, Bryan!
[root@localhost java]# curl 'http://localhost:8082/publish?helloStr=hi%20there'
published
[root@localhost java]# ./run.sh lesson03.exercise.Hello Bryan
02:50:15.525 [main] DEBUG io.jaegertracing.thrift.internal.senders.ThriftSenderFactory - Using the UDP Sender to send spans to the agent.
02:50:15.628 [main] DEBUG io.jaegertracing.internal.senders.SenderResolver - Using sender UdpSender()
02:50:15.723 [main] INFO io.jaegertracing.Configuration - Initialized tracer=JaegerTracer(version=Java-1.1.0, serviceName=hello-world, reporter=CompositeReporter(reporters=[RemoteReporter(sender=UdpSender(), closeEnqueueTimeout=1000), LoggingReporter(logger=Logger[io.jaegertracing.internal.reporters.LoggingReporter])]), sampler=ConstSampler(decision=true, tags={sampler.type=const, sampler.param=true}), tags={hostname=localhost.localdomain, jaeger.version=Java-1.1.0, ip=127.0.0.1}, zipkinSharedRpcSpan=false, expandExceptionLogs=false, useTraceId128Bit=false)
02:50:17.020 [main] INFO io.jaegertracing.internal.reporters.LoggingReporter - Span reported: 364c6519897fe690:2b596be32a3c8e7d:364c6519897fe690:1 - formatString
02:50:17.091 [main] INFO io.jaegertracing.internal.reporters.LoggingReporter - Span reported: 364c6519897fe690:8506516ad419caaf:364c6519897fe690:1 - printHello
02:50:17.091 [main] INFO io.jaegertracing.internal.reporters.LoggingReporter - Span reported: 364c6519897fe690:364c6519897fe690:0:1 - say-hello

Jaeger UI でソース通りにトレース情報が生成できているかを確認

opentracing_turorial_003_001.png

opentracing_turorial_003_002.png

opentracing_turorial_003_003.png

README.md で気になった箇所を抜粋

  • The tracing instrumentation uses inject and extract to pass the span context through the RPC calls.
  • トレーシングのインストルメンテーションでは、RPCコールを介してスパンコンテキストを渡すためにinjectとextractを使用しています。

  • Instrumenting the Client In the Hello#formatString() function we already create a child span. In order to pass its context over the HTTP request we need to call tracer.inject before building the HTTP request in Hello#getHttp()
  • クライアントのインストルメンテーション Hello#formatString() 関数では、すでに子スパンを作成しています。そのコンテキストを HTTP リクエストに渡すためには、Hello#getHttp() で HTTP リクエストをビルドする前に tracer.inject を呼び出す必要があります。

Inject and Extract に関しては、下記も読んで理解を深めておいたほうがよさそうです。

Lesson 4のソースをビルドして実行してみる

README.md を見つつ、ソースを編集

src/main/java/lesson04/exercise/Hello.java
package lesson04.exercise;

import java.io.IOException;

import com.google.common.collect.ImmutableMap;

import io.jaegertracing.internal.JaegerTracer;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import io.opentracing.tag.Tags;
import lib.Tracing;

import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class Hello {

    private final Tracer tracer;
    private final OkHttpClient client;

    private Hello(Tracer tracer) {
        this.tracer = tracer;
        this.client = new OkHttpClient();
    }

    private String getHttp(int port, String path, String param, String value) {
        try {
            HttpUrl url = new HttpUrl.Builder().scheme("http").host("localhost").port(port).addPathSegment(path)
                    .addQueryParameter(param, value).build();
            Request.Builder requestBuilder = new Request.Builder().url(url);

            Tags.SPAN_KIND.set(tracer.activeSpan(), Tags.SPAN_KIND_CLIENT);
            Tags.HTTP_METHOD.set(tracer.activeSpan(), "GET");
            Tags.HTTP_URL.set(tracer.activeSpan(), url.toString());
            //tracer.inject(tracer.activeSpan().context(), Format.Builtin.HTTP_HEADERS, new RequestBuilderCarrier(requestBuilder));
            tracer.inject(tracer.activeSpan().context(), Format.Builtin.HTTP_HEADERS, Tracing.requestBuilderCarrier(requestBuilder));

            Request request = requestBuilder.build();
            Response response = client.newCall(request).execute();
            if (response.code() != 200) {
                throw new RuntimeException("Bad HTTP result: " + response);
            }
            return response.body().string();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void sayHello(String helloTo, String greeting) {
        // Operation: tracer.buildSpan("operationName");
        // Generating trace information: tracer.buildSpan();
        // Trace start: .start();
        Span span = tracer.buildSpan("say-hello").start();
        try (Scope scope = tracer.scopeManager().activate(span)) {
            // Set span tags: span.setTag("key", value);
            span.setTag("hello-to", helloTo);
            // Set Baggage: span.setBaggageItem("key", value);
            span.setBaggageItem("greeting", greeting);

            String helloStr = formatString(helloTo);
            printHello(helloStr);
        } finally {
            span.finish(); // Trace end: span.finish();
        }
    }

    private String formatString(String helloTo) {
        // Operation: tracer.buildSpan("operationName");
        // Generating trace information: tracer.buildSpan();
        // Trace start: .start();
        Span span = tracer.buildSpan("formatString").start();
        try (Scope scope = tracer.scopeManager().activate(span)) {
            // In order to pass its context over the HTTP request we need to call tracer.
            // inject before building the HTTP request in Hello#getHttp()
            String helloStr = getHttp(8081, "format", "helloTo", helloTo);
            // Set span logs: span.log(ImmutableMap.of("key1", "value1", "key2", "value2"));
            span.log(ImmutableMap.of("event", "string-format", "value", helloStr));
            return helloStr;
        } finally {
            span.finish(); // Trace end: span.finish();
        }
    }

    private void printHello(String helloStr) {
        // Operation: tracer.buildSpan("operationName");
        // Generating trace information: tracer.buildSpan();
        // Trace start: .start();
        Span span = tracer.buildSpan("printHello").start();
        try (Scope scope = tracer.scopeManager().activate(span)) {
            // In order to pass its context over the HTTP request we need to call tracer.
            // inject before building the HTTP request in Hello#getHttp()
            getHttp(8082, "publish", "helloStr", helloStr);
            // Set span logs: span.log(ImmutableMap.of("key1", "value1"));
            span.log(ImmutableMap.of("event", "println"));
        } finally {
            span.finish(); // Trace end: span.finish();
        }
    }

    public static void main(String[] args) {
        if (args.length != 2) {
            throw new IllegalArgumentException("Expecting two arguments, helloTo and greeting");
        }
        String helloTo = args[0];
        String greeting = args[1];
        // Service: Tracing.init("serviceName");
        try (JaegerTracer tracer = Tracing.init("hello-world")) {
            new Hello(tracer).sayHello(helloTo, greeting);
        }
    }
}
src/main/java/lesson04/exercise/Formatter.java
package lesson04.exercise;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;

import com.google.common.collect.ImmutableMap;
import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.setup.Environment;
import io.jaegertracing.internal.JaegerTracer;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
import lib.Tracing;

public class Formatter extends Application<Configuration> {
    private final Tracer tracer;

    private Formatter(Tracer tracer) {
        this.tracer = tracer;
    }

    @Path("/format")
    @Produces(MediaType.TEXT_PLAIN)
    public class FormatterResource {

        @GET
        public String format(@QueryParam("helloTo") String helloTo, @Context HttpHeaders httpHeaders) {
            // Operation: Tracing.startServerSpan(tracer, httpHeaders, "operationName");
            Span span = Tracing.startServerSpan(tracer, httpHeaders, "format");
            try (Scope scope = tracer.scopeManager().activate(span)) {
                // Get baggage: span.getBaggageItem(value);
                String greeting = span.getBaggageItem("greeting");
                if (greeting == null) {
                    greeting = "Hello";
                }
                String helloStr = String.format("%s, %s!", greeting, helloTo);
                // Set span logs: span.log(ImmutableMap.of("key1", "value1", "key2", "value2"));
                span.log(ImmutableMap.of("event", "string-format", "value", helloStr));
                return helloStr;
            } finally {
                span.finish(); //Trace end: span.finish();
            }
        }

    }

    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        environment.jersey().register(new FormatterResource());
    }

    public static void main(String[] args) throws Exception {
        System.setProperty("dw.server.applicationConnectors[0].port", "8081");
        System.setProperty("dw.server.adminConnectors[0].port", "9081");
        // Service: Tracing.init("serviceName");
        try (JaegerTracer tracer = Tracing.init("formatter")) {
            new Formatter(tracer).run(args);
        }
    }
}
src/main/java/lesson04/exercise/Publisher.java
package lesson04.exercise;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;

import com.google.common.collect.ImmutableMap;

import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.setup.Environment;
import io.jaegertracing.internal.JaegerTracer;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
import lib.Tracing;

public class Publisher extends Application<Configuration> {

    private final Tracer tracer;

    private Publisher(Tracer tracer) {
        this.tracer = tracer;
    }

    @Path("/publish")
    @Produces(MediaType.TEXT_PLAIN)
    public class PublisherResource {

        @GET
        public String format(@QueryParam("helloStr") String helloStr, @Context HttpHeaders httpHeaders) {
            // Operation: Tracing.startServerSpan(tracer, httpHeaders, "operationName");
            Span span = Tracing.startServerSpan(tracer, httpHeaders, "publish");
            try (Scope scope = tracer.scopeManager().activate(span)) {
                System.out.println(helloStr);
                // Set span logs: span.log(ImmutableMap.of("key1", "value1", "key2", "value2"));
                span.log(ImmutableMap.of("event", "println", "value", helloStr));
                return "published";
            } finally {
                span.finish(); //Trace end: span.finish();
            }
        }
    }

    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        environment.jersey().register(new PublisherResource());
    }

    public static void main(String[] args) throws Exception {
        System.setProperty("dw.server.applicationConnectors[0].port", "8082");
        System.setProperty("dw.server.adminConnectors[0].port", "9082");
        // Service: Tracing.init("serviceName");
        try (JaegerTracer tracer = Tracing.init("publisher")) {
            new Publisher(tracer).run(args);
        }
    }
}

ビルド

[root@localhost java]# mvn clean package

実行

Lesson4.log
[root@localhost java]# ./run.sh lesson04.exercise.Formatter server
[root@localhost java]# ./run.sh lesson04.exercise.Publisher server
[root@localhost java]# ./run.sh lesson04.exercise.Hello Bryan Bonjour
07:24:05.133 [main] DEBUG io.jaegertracing.thrift.internal.senders.ThriftSenderFactory - Using the UDP Sender to send spans to the agent.
07:24:05.263 [main] DEBUG io.jaegertracing.internal.senders.SenderResolver - Using sender UdpSender()
07:24:05.369 [main] INFO io.jaegertracing.Configuration - Initialized tracer=JaegerTracer(version=Java-1.1.0, serviceName=hello-world, reporter=CompositeReporter(reporters=[RemoteReporter(sender=UdpSender(), closeEnqueueTimeout=1000), LoggingReporter(logger=Logger[io.jaegertracing.internal.reporters.LoggingReporter])]), sampler=ConstSampler(decision=true, tags={sampler.type=const, sampler.param=true}), tags={hostname=localhost.localdomain, jaeger.version=Java-1.1.0, ip=127.0.0.1}, zipkinSharedRpcSpan=false, expandExceptionLogs=false, useTraceId128Bit=false)
07:24:07.408 [main] INFO io.jaegertracing.internal.reporters.LoggingReporter - Span reported: b31a103acb8be300:180478edc119febe:b31a103acb8be300:1 - formatString
07:24:08.217 [main] INFO io.jaegertracing.internal.reporters.LoggingReporter - Span reported: b31a103acb8be300:f1dcd8c815cb525a:b31a103acb8be300:1 - printHello
07:24:08.217 [main] INFO io.jaegertracing.internal.reporters.LoggingReporter - Span reported: b31a103acb8be300:b31a103acb8be300:0:1 - say-hello

Jaeger UI でソース通りにトレース情報が生成できているかを確認

opentracing_turorial_004_001.png

最後に

以上でOpenTracing チュートリアルは完了です。
もっと詳しい使い方に関しては、下記を参照願います。

opentracing.io/docs

opentracing.io/guides/java

参考URL

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