20200215のJavaに関する記事は6件です。

SpringBoot2.2.4の内蔵TomcatでSSIServletを使う

対象読者

SpringBootを使ったWebサービスを開発しており、内蔵Tomcatを利用している。
また、SSI (Server Side Include) を利用しているため、SSIServletを使用している。

TL;DR

Tomcat 9.0.30ではSSI関連のモジュールが外部ライブラリ化していますので、
以下のTomcat SSIライブラリも依存関係に追加しなければならなくなりました。

https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-ssi

経緯

JVNVU#98104709 Apache Tomcat の複数の脆弱性に対するアップデート
http://jvn.jp/vu/JVNVU98104709/index.html
により、SpringBootの内蔵Tomcatをバージョンアップする必要が出てきました。

そこで、SpringBootのバージョンを最新のリリースバージョン2.2.4.RELEASEへと更新することにしました。

SpringBoot 2.2.4RELEASEの内蔵Tomcatのバージョン

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#dependency-versions

公式ドキュメントによると、内蔵されたTomcatのバージョンは9.0.30です。

Tomcat 9.0.30では、SSI関連モジュールが外部ライブラリ化されている!

ここで、Tomcat 9.0.30のChangeLogを参照すると、

Moved server-side include (SSI) module into a separate JAR library. (schultz)

と記載されています。

実際にbuild.gradleファイルにてSpringBootのバージョンを2.2.4.RELEASEへと変更すると、
SSIServletが見つからなくなり、ビルドできなくなります。

どこに移動したかが書いていないので、MVN Repositoryを探索しました。

Tomcat SSIライブラリに分割されていた

https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-ssi

MVN Repository内にて、Tomcat SSIライブラリを発見しました。

Spring Bootの内蔵Tomcatにバージョンをあわせて9.0.30を選択して、build.gradledependenciesに追加したところ、問題なくSSIServletを見つけられるようになり、ビルドが通りました。

まとめ

本記事では、Spring Boot 2.2.4.RELEASE内蔵のTomcat 9.0.30にて、SSIを使用するための依存ライブラリを紹介しました。

お役に立てれば幸いです。

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

JavaでABNF書いてメールアドレスのぱーす

プログラム中でメールアドレスのパースは難しいとか言われているのでABNFのフル機能のほんものを実装してみた。
いろいろ詰め込んだ結果、JSONやJSON Pointer、JSON Patchまで簡単に実装できる。

IETFのRFCではABNFがよく使われている。メールだったりJSONなんかが有名で、IPアドレスやURLの書き方までABNFだったりする。
その割にあまり一般的なツールやライブラリとして出回っているのは数えるほどしかみたことないのでさっくりと作ってみた。

構造はParserを拡張してびるだー的なものを作って、びるだーでテキストからオブジェクトに変換するだけ。
ABNF自体もABNFで書かれているが、これは最初なのでビルダーだけで作る。
JSONなども書式を指定すればパーサができるので出力されるパーツを組み上げるビルダー的なところを組み込めばできあがり。

SoftLibRFC にRFCの一部ABNFをまとめているのでそれを使ってみるところの紹介。
必要なのは、SoftLibRFC,SoftLibABNF,SoftLib の3つ。SoftLibJSONは使わない。
現状ソースコードのみなので各自適度に使ってみたら。

net.siisise.abnf.rfc.IMF5322 というクラスがRFCのメールのABNFでRFC 5322ほぼそのまま。
IMF5322.angleAddr くらいがメールアドレスのみの定義にちょうどいいかな。
定義はREG という変数に格納されているのでpublicにしてないのはそこから取ってきてもいいしpublicにしてもいい。

boolean b = IMF5322.REG.ref("angle-addr").eq(メールアドレス);

くらいでbooleanな判定ができるかな。
個別のABNFはABNFクラス。
boolean eq(String) 完全一致 やboolean is(String) 先頭一致といったメソッドがあるのでお試しください。
Packet というクラスとあわせると、いろいろできるのだけど今回は省略。
ABNF同士をかけあわせて大きくしていくことができる。

まとめて使うには

ABNFReg に名前をつけて登録できるし、定義の中で参照も可能。
.rule(name,ABNF)と.rule(name,rule) .rule(rule) はどれもABNFの登録。
.ref(String)や.href(String)は名前で登録したABNFを持ってくる。
登録順序の関係が前後することがあるので登録していないものを取ってくるかこないかの違いがある。
.rulelist(String)で複数定義の一括登録も可能。改行が必要かもしれない。

拡張していくといつのまにかJSONも乗っかるので不思議。

SoftLibRFC
https://github.com/okomeki/SoftLibRFC
SoftLibABNF
https://github.com/okomeki/SoftLibABNF
SoftLib
https://github.com/okomeki/SoftLib
RFC 5234 ABNF
https://tools.ietf.org/html/rfc5234
RFC 8259 JSON
https://tools.ietf.org/html/rfc8259

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

五目並べゲーム(作成中)

(ルール)

・5×5(変更可)のマス目に黒と白が交互に石を置いていく
・縦、横、斜めのマスが同色石によってすべて埋まった時点で勝利

(各ファイル)

・Stoneクラス
→石の情報を管理
・Boardクラス
→碁盤情報を管理
・Judgeクラス (うまくいかないため現在作成中)
→勝利判定を管理
・Constantインターフェイス
→定数を管理
・Gameクラス
→ゲームとしてまとめる

Stone.java
package Gomokunarabe;

public class Stone implements Constant{
    Board board = new Board();


    //石が正常に置けたらtrue,置けなかったらfalse
    private boolean active;
    public boolean getActive() {
        return active;
    }
    public void setActive(boolean active) {
        this.active = active;
    }


    //石の位置に関するフィールド
    private int tatePosition;
    private int yokoPosition;
    public int getTatePosition() {
        return tatePosition;
    }
    public void setTatePosition(int tatePosition) {
        this.tatePosition = tatePosition;
    }
    public int getYokoPosition() {
        return yokoPosition;
    }
    public void setYokoPosition(int yokoPosition) {
        this.yokoPosition = yokoPosition;
    }


    //石配置処理
    @SuppressWarnings("resource")
    public void stoneConfig(boolean logic) {
        System.out.println("石の位置を入力してください");
        try {
        int tatePosition = new java.util.Scanner(System.in).nextInt();
        int yokoPosition = new java.util.Scanner(System.in).nextInt();
        if((tatePosition>=0&&yokoPosition>=0)&&(tatePosition<HEIGHT_LENGTH && yokoPosition<WIDTH_LENGTH)) {
            if(board.getPosition(tatePosition, yokoPosition) == null) {
                setTatePosition(tatePosition);
                setYokoPosition(yokoPosition);
                setActive(true);
                board.board(getTatePosition(), getYokoPosition(), logic);
            }
            else {
                setActive(false);
                System.out.println("石が置かれていない場所を選択してください");
            }
        }
        else {
            setActive(false);
            System.out.println("0~"+(HEIGHT_LENGTH-1)+"のどれかで入力してください");
        }
        }
        catch(Exception e) {
            setActive(false);
            System.out.println("整数で入力してください");
        }
    }

}
Board.java
package Gomokunarabe;

public class Board implements Constant{
    //石の位置を保存
    private String[][] position = new String[HEIGHT_LENGTH][WIDTH_LENGTH];
    public void setPosition(int tate, int yoko, String stone) {
        position[tate][yoko] = stone;
    }
    public String getPosition(int tate, int yoko) {
        return position[tate][yoko];
    }

    //trueなら黒、falseなら白
    private Boolean Logic;
    public Boolean isLogic() {
        return Logic;
    }
    public void setLogic(Boolean logic) {
        Logic = logic;
    }


    //碁盤作成メソッド
    public void board(int tate, int yoko, boolean logic) {
        //ロジックで白か黒かを判定
        setLogic(logic);

        for(int i=0; i<HEIGHT_LENGTH; i++) {
            //for文の変数iは縦を意味する
            for(int j=0; j<WIDTH_LENGTH; j++) {
                //for文の変数jは横を意味する
                if((i==tate) && (j==yoko)) {
                    if(isLogic()==true) {
                    setPosition(tate,yoko,BLACK_STONE);
                    System.out.print(getPosition(tate,yoko));
                    }
                    else{
                        setPosition(tate,yoko,WHITE_STONE);
                        System.out.print(getPosition(tate,yoko));
                    }
                }
                else if(position[i][j] == null){
                System.out.print("□");
                }
                else {
                    System.out.print(position[i][j]);
                }
            }
            System.out.println("");
        }
    }


    public void firstShowBoard() {
        //碁盤の数字表示
        for(int i=0; i<HEIGHT_LENGTH;i++) {
            if(i==(HEIGHT_LENGTH-1)) {
                System.out.println(i);
                }
            else if(i==0) {
                System.out.print(" "+i+",");
            }
            else {
                System.out.print(i + ",");
                }
                }
        for(int j=0; j<HEIGHT_LENGTH; j++) {
            System.out.println(j);
        }

        for(int i=0; i<HEIGHT_LENGTH;i++) {
            for(int j=0; j<WIDTH_LENGTH; j++) {
                System.out.print("□");
            }
            System.out.println("");
        }

        }
    }
Judge.java
package Gomokunarabe;

public class Judge implements Constant{

    Board board = new Board();
    //private Boolean logic = null;
    private int winBlack = 0;
    private int winWhite = 0;

    private boolean logic;

    public int getWinBlack() {
        return winBlack;
    }
    public void setWinBlack(int winBlack) {
        this.winBlack += winBlack;
    }
    public void setWinBlackL(int winBlack) {
        this.winBlack = winBlack;
    }
    public int getWinWhite() {
        return winWhite;
    }
    public void setWinWhite(int winWhite) {
        this.winWhite += winWhite;
    }
    public void setWinWhiteL(int winWhite) {
        this.winWhite = winWhite;
    }

    public Boolean isLogic() {
        return logic;
    }
    public void setLogic(boolean logic) {
        this.logic = logic;
    }

    //勝利判定
    public Boolean judgement() {
        //戻り値tureで黒の勝ち、falseで白の勝ち

        //横列の勝利判定
        for(int i=0; i<HEIGHT_LENGTH; i++){
            for(int j=0; j<HEIGHT_LENGTH; j++) {
                if(board.getPosition(i, j) == BLACK_STONE) {
                    setWinBlack(1);
                    if(getWinBlack() == HEIGHT_LENGTH) {
                        setLogic(true);
                        break;
                    }
                }
                else if(board.getPosition(i, j) == WHITE_STONE) {
                    setWinWhite(1);
                    if(getWinWhite() == HEIGHT_LENGTH) {
                        setLogic(false);
                        break;
                    }
                }
                else {}
            }
            setWinBlackL(0);
            setWinWhiteL(0);
        }

        //縦列の勝利判定
        for(int i=0; i<HEIGHT_LENGTH; i++){
            for(int j=0; j<HEIGHT_LENGTH; j++) {
                if(board.getPosition(j, i) == BLACK_STONE) {
                    setWinBlack(1);
                    if(getWinBlack() == HEIGHT_LENGTH) {
                        setLogic(true);
                        break;
                    }
                }
                else if(board.getPosition(j, i) == WHITE_STONE) {
                    setWinWhite(1);
                    if(getWinWhite() == HEIGHT_LENGTH) {
                        setLogic(false);
                        break;
                    }
                }
                else {}
            }
            setWinBlackL(0);
            setWinWhiteL(0);
        }

        for(int i=0; i<HEIGHT_LENGTH; i++){
            for(int j=0; j<HEIGHT_LENGTH; j++) {
                if((i==j) && board.getPosition(i, j) == BLACK_STONE) {
                    setWinBlack(1);
                    if(getWinBlack() == HEIGHT_LENGTH) {
                        setLogic(true);
                        break;
                    }
                }
                else if((i==j) && board.getPosition(i, j) == WHITE_STONE) {
                    setWinWhite(1);
                    if(getWinWhite() == HEIGHT_LENGTH) {
                        setLogic(false);
                        break;
                    }
                }
                else {}
            }
            }
            setWinBlackL(0);
            setWinWhiteL(0);

        //右斜めの勝利判定
        for(int j=0; j<HEIGHT_LENGTH; j++){
            for(int i=(HEIGHT_LENGTH-1); i>=0; i--) {
                if(board.getPosition(j, (i-j)) == BLACK_STONE) {
                    setWinBlack(1);
                    if(getWinBlack() == HEIGHT_LENGTH) {
                        setLogic(true);
                        break;
                    }
                }
                else if(board.getPosition(j, (i-j)) == WHITE_STONE) {
                    setWinWhite(1);
                    if(getWinWhite() == HEIGHT_LENGTH) {
                        setLogic(false);
                        break;
                    }
                }
                else {}
        }
            }
        setWinBlackL(0);
        setWinWhiteL(0);


        return isLogic();
    }
}

Constant.java
package Gomokunarabe;

public interface Constant{
    /*
     * 碁盤の長さに関する定数を変えることで
     * 升目を変更
     * */

    //碁盤の縦の長さ
    int HEIGHT_LENGTH = 5;

    //碁盤の横の長さ
    int WIDTH_LENGTH = 5;

    //黒い石
    String BLACK_STONE = "●";
    //白い石
    String WHITE_STONE = "〇";

}

Game.java
package Gomokunarabe;

public class Game {
    Stone stone = new Stone();
    Judge judge = new Judge();
    Board board = new Board();

    private boolean logic = true;
    public boolean isLogic() {
        return logic;
    }
    public void setLogic(boolean logic) {
        this.logic = logic;
    }

    private int loop=1;
    public int getLoop() {
        return loop;
    }
    public void setLoop(int loop) {
        this.loop = loop;
    }

    public void gameStart() {
        System.out.println("★五目並べ★");
        board.firstShowBoard();

        while(isLogic()==true) {
            //loopが奇数のときは黒
            if(getLoop()%2 == 1) {
                System.out.println("黒の番です");
                stone.stoneConfig(true);
                if(stone.getActive()==true) {
                setLoop(2);
                }
                else {
                setLoop(1);
                }
            }
            //loopが偶数の時は
            else{
                System.out.println("白の番です");
                stone.stoneConfig(false);

                if(stone.getActive()==true) {
                    setLoop(1);
                    }
                else {
                setLoop(2);
                }
            }

        }
    }


}

Main.java
package Gomokunarabe;

public class Main {

    public static void main(String[] args) {
        Game game = new Game();
        game.gameStart();
    }
}

【実行結果】

★五目並べ★
0,1,2,3,4
0
1
2
3
4
□□□□□
□□□□□
□□□□□
□□□□□
□□□□□
黒の番です
石の位置を入力してください
1
1
□□□□□
□●□□□
□□□□□
□□□□□
□□□□□
白の番です
石の位置を入力してください
0
0
〇□□□□
□●□□□
□□□□□
□□□□□
□□□□□
黒の番です
石の位置を入力してください
1
0
〇□□□□
●●□□□
□□□□□
□□□□□
□□□□□
白の番です
石の位置を入力してください
0
2
〇□〇□□
●●□□□
□□□□□
□□□□□
□□□□□
黒の番です
石の位置を入力してください
1
2
〇□〇□□
●●●□□
□□□□□
□□□□□
□□□□□
白の番です
石の位置を入力してください
0
4
〇□〇□〇
●●●□□
□□□□□
□□□□□
□□□□□

(-以下省略)

・勝利判定がうまくいかないので今後実装予定

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

JAXB Reference implementation と DatatypeConverterImpl の行方

概要

  • JAXB (Java Architecture for XML Binding) の公式に近い Reference implementation (参照実装) の jar とソースコードをバージョン毎に追いかける
  • 動作確認環境: Java 11 (AdoptOpenJDK 11.0.6+10) + Gradle 6.1

一覧表

バージョン パッケージ名 Gradle implementation 記述
2.3.1 javax.xml.bind javax.xml.bind:jaxb-api:2.3.1
2.3.2 javax.xml.bind jakarta.xml.bind:jakarta.xml.bind-api:2.3.2
2.3.3 javax.xml.bind ?
2.4.0 javax.xml.bind javax.xml.bind:jaxb-api:2.4.0-b180830.0359
3.0.0 jakarta.xml.bind jakarta.xml.bind:jakarta.xml.bind-api:3.0.0-RC1

JAXB 2.3.1

Maven Repository: javax.xml.bind » jaxb-api » 2.3.1

build.gradle ファイルを用意。
dependencies に implementation 'javax.xml.bind:jaxb-api:2.3.1' を記述することで使用できる。

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

repositories {
  mavenCentral()
}

dependencies {
  implementation 'javax.xml.bind:jaxb-api:2.3.1'
}

application {
  mainClassName = 'Sample'
}

JAXB を使用するサンプル src/main/java/Sample.java を用意。

import java.security.SecureRandom;
import javax.xml.bind.DatatypeConverter; // JAXB のクラス

public class Sample {
  public static void main(String[] args) {
    byte[] bytes = new byte[32];
    new SecureRandom().nextBytes(bytes);
    // JAXB のクラスを使用
    String str = DatatypeConverter.printHexBinary(bytes);
    System.out.println(str);
  }
}

gradle run で実行。

$ gradle run

> Task :run
20982DD2EA72EFA26763A6256209BCDC0F500C7F6BC26993FCB9690022E109FD

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

実行したサンプルは JAXB の内部で DatatypeConverterImpl クラスを使用している。

DatatypeConverterImpl クラスのソースコード。パッケージは javax.xml.bind となる。
GitHub 等のリポジトリでソースコードが見つからなかったため https://repo1.maven.org/maven2/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1-sources.jar を展開した。

private static final char[] hexCode = "0123456789ABCDEF".toCharArray();

public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length * 2);
    for (byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}

JAXB 2.3.2 (Jakarta XML Binding 2.3.2)

Eclipse Enterprise for Java (EE4J) からの最初のリリース。
EE4J は Oracle から Java EE を移管するプロジェクト。

2.3.2 | projects.eclipse.org

First release from Eclipse. No API or implementation changes, just new licensing and Maven coordinates.

2.3.2 | projects.eclipse.org

Release Date:
Friday, December 14, 2018

Deliverables:
Project binaries, sources and javadoc available through Maven repository.

Compatibility:
This is the first release of Eclipse Project for JAXB, which is fully binary compatible with previous releases of the JavaTM Architecture for XML Binding API Implementation released by Oracle.

Maven Repository: jakarta.xml.bind » jakarta.xml.bind-api » 2.3.2

build.gradle の dependencies に以下を記述することで使用できる。

build.gradle
dependencies {
  implementation 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.2'
}

DatatypeConverterImpl クラスのソースコード。パッケージは javax.xml.bind となる。

https://github.com/eclipse-ee4j/jaxb-api/blob/2.3.2/jaxb-api/src/main/java/javax/xml/bind/DatatypeConverterImpl.java

private static final char[] hexCode = "0123456789ABCDEF".toCharArray();

public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length * 2);
    for (byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}

JAXB 2.3.3 (Jakarta XML Binding 2.3.3)

2.3.3 | projects.eclipse.org

First release going through the JESP. Bug fix release of 2.3.2.

Release Date:
Wednesday, February 19, 2020

Maven Central Repository には見つからなかった。

DatatypeConverterImpl クラスのソースコード。パッケージは javax.xml.bind となる。

https://github.com/eclipse-ee4j/jaxb-api/blob/2.3.3/jaxb-api/src/main/java/javax/xml/bind/DatatypeConverterImpl.java

private static final char[] hexCode = "0123456789ABCDEF".toCharArray();

public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length * 2);
    for (byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}

JAXB 2.4.0

Maven Repository: javax.xml.bind » jaxb-api » 2.4.0-b180830.0359

build.gradle の dependencies に以下を記述することで使用できる。

build.gradle
dependencies {
  implementation 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359'
}

DatatypeConverterImpl クラスのソースコード。パッケージは javax.xml.bind となる。
GitHub 等のリポジトリでソースコードが見つからなかったため https://repo1.maven.org/maven2/javax/xml/bind/jaxb-api/2.4.0-b180830.0359/jaxb-api-2.4.0-b180830.0359-sources.jar を展開した。

private static final char[] hexCode = "0123456789ABCDEF".toCharArray();

public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length * 2);
    for (byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}

似たようなバージョンのファイルが他の場所にあるが、パッケージが com.sun.xml.bind になっている別物。

Maven Repository: com.sun.xml.bind » jaxb-ri » 2.4.0-b180830.0438

compile group: 'com.sun.xml.bind', name: 'jaxb-ri', version: '2.4.0-b180830.0438', ext: 'pom'

https://github.com/javaee/jaxb-v2/blob/2.4.0-b180830.0438/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/DatatypeConverterImpl.java

@Deprecated
private static final char[] hexCode = "0123456789ABCDEF".toCharArray();

@Deprecated
public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length * 2);
    for (byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}

GlassFish JAXB の DatatypeConverterImpl も同じく com.sun.xml.bind パッケージだった。

Maven Repository: org.glassfish.jaxb » jaxb-runtime » 2.4.0-b180830.0438

JAXB 3.0.0 (Jakarta XML Binding 3.0)

このバージョンからパッケージ名が javax.xml.bind から jakarta.xml.bind に変更されている。

3.0 | projects.eclipse.org

Change javax.* package name to jakarta.*

Release Date:
Friday, February 7, 2020

Release Type:
Major release (API breakage)

Jakarta EE の Web サイトには 「under development (開発中)」 と書かれている。

Jakarta XML Binding 3.0 (under development) | The Eclipse Foundation

Maven Repository: jakarta.xml.bind » jakarta.xml.bind-api » 3.0.0-RC1

build.gradle の dependencies に以下を記述することで使用できる。

build.gradle
dependencies {
  implementation 'jakarta.xml.bind:jakarta.xml.bind-api:3.0.0-RC1'
}

JAXB を使用するサンプル src/main/java/Sample.java を用意。
今回のサンプルではパッケージ名を javax.xml.bind から jakarta.xml.bind に変更しただけで動作した。

import java.security.SecureRandom;
import jakarta.xml.bind.DatatypeConverter; // JAXB のクラス

public class Sample {
  public static void main(String[] args) {
    byte[] bytes = new byte[32];
    new SecureRandom().nextBytes(bytes);
    // JAXB のクラスを使用
    String str = DatatypeConverter.printHexBinary(bytes);
    System.out.println(str);
  }
}

DatatypeConverterImpl クラスのソースコード。パッケージは jakarta.xml.bind となる。

https://github.com/eclipse-ee4j/jaxb-api/blob/3.0.0-RC1/jaxb-api/src/main/java/jakarta/xml/bind/DatatypeConverterImpl.java

private static final char[] hexCode = "0123456789ABCDEF".toCharArray();

public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length * 2);
    for (byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}

参考資料

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

Java のバイト配列を16進数表記の文字列に変換する

方法

JAXB の javax.xml.bind.DatatypeConverter クラスを利用する。

DatatypeConverter (Java Platform SE 8 )

public static String printHexBinary(byte[] val)
バイト配列を文字列に変換します。

JAXB は Java 11 から Java SE 標準ライブラリに入っていない

JAXB は Java SE 6 以降の標準ライブラリに入っていた。

JAXB Release Documentation - Frequently Asked Questions

Q: Which version of J2SE does JAXB 2.0 require?
A: Java SE 6 or higher.

JAXB は Java SE 11 以降の標準ライブラリからは削除されている。

Java Platform, Standard Edition Oracle JDK 移行ガイド,リリース11

JDK 11では、Java EEおよびCORBAモジュールが削除されました。これらのモジュールはJDK 9で削除予定の非推奨となりました。

サンプルコード

ソースコード一覧

環境は Java 11 + Gradle 6.1 を想定。

├── build.gradle
└── src
    └── main
        └── java
            └── HexString.java

build.gradle

JAXB を使うため dependencies に javax.xml.bind:jaxb-api:2.3.1 を追加している。

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

repositories {
  mavenCentral()
}

dependencies {
  // https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api
  implementation 'javax.xml.bind:jaxb-api:2.3.1'
}

application {
  mainClassName = 'HexString'
}

src/main/java/HexString.java

ランダム生成した byte の配列を DatatypeConverter クラスで16進数表記の文字列に変換するサンプルコード。

import java.security.SecureRandom;
import javax.xml.bind.DatatypeConverter;

public class HexString {

  public static void main(String[] args) {
    int num = 0;
    if (args.length > 0) {
      num = Integer.parseInt(args[0]);
    }
    var r = random(num);
    System.out.println("文字列: " + r);
    System.out.println("文字数: " + r.length());
    System.out.println("16進数字表記チェック: " + r.matches("\\p{XDigit}+")); // 正規表現でチェック
  }

  /**
   * ランダムな16進数表記の文字列を返します。
   * @param num 生成する文字数
   * @return ランダムな16進数表記文字列
   */
  public static String random(int num) {
    var random = new SecureRandom();
    byte[] bytes = new byte[num / 2];
    random.nextBytes(bytes);
    return DatatypeConverter.printHexBinary(bytes);
  }
}

実行例

Java 11 (AdoptOpenJDK) + Gradle 6.1 で実行。

$ gradle run --args=32

> Task :run
文字列: A01CCE3E4133D822CCBCA8247353C48F
文字数: 32
16進数字表記チェック: true

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

DatatypeConverter.printHexBinary の中身

実際の処理は DatatypeConverterImpl クラスの printHexBinary メソッドが担っている。

Maven Repository: javax.xml.bind » jaxb-api » 2.3.1 からダウンロードできる jaxb-api-2.3.1-sources.jar ファイルの中を見ると、 javax/xml/bind/DatatypeConverterImpl.java というファイルがある。

printHexBinary メソッドが byte 配列を16進数表記の文字列に変換している。

private static final char[] hexCode = "0123456789ABCDEF".toCharArray();

public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length * 2);
    for (byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}

参考資料

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

Java 14新機能まとめ

Java 14新機能まとめ

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