- 投稿日:2020-02-15T23:41:43+09:00
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.gradle
のdependencies
に追加したところ、問題なくSSIServlet
を見つけられるようになり、ビルドが通りました。まとめ
本記事では、Spring Boot 2.2.4.RELEASE内蔵のTomcat 9.0.30にて、SSIを使用するための依存ライブラリを紹介しました。
お役に立てれば幸いです。
- 投稿日:2020-02-15T18:09:48+09:00
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
- 投稿日:2020-02-15T14:38:54+09:00
五目並べゲーム(作成中)
(ルール)
・5×5(変更可)のマス目に黒と白が交互に石を置いていく
・縦、横、斜めのマスが同色石によってすべて埋まった時点で勝利(各ファイル)
・Stoneクラス
→石の情報を管理
・Boardクラス
→碁盤情報を管理
・Judgeクラス (うまくいかないため現在作成中)
→勝利判定を管理
・Constantインターフェイス
→定数を管理
・Gameクラス
→ゲームとしてまとめるStone.javapackage 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.javapackage 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.javapackage 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.javapackage Gomokunarabe; public interface Constant{ /* * 碁盤の長さに関する定数を変えることで * 升目を変更 * */ //碁盤の縦の長さ int HEIGHT_LENGTH = 5; //碁盤の横の長さ int WIDTH_LENGTH = 5; //黒い石 String BLACK_STONE = "●"; //白い石 String WHITE_STONE = "〇"; }Game.javapackage 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.javapackage 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
〇□〇□〇
●●●□□
□□□□□
□□□□□
□□□□□(-以下省略)
・勝利判定がうまくいかないので今後実装予定
- 投稿日:2020-02-15T14:01:16+09:00
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.gradleplugins { 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 を移管するプロジェクト。First release from Eclipse. No API or implementation changes, just new licensing and Maven coordinates.
Release Date:
Friday, December 14, 2018Deliverables:
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.gradledependencies { implementation 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.2' }DatatypeConverterImpl クラスのソースコード。パッケージは javax.xml.bind となる。
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)
First release going through the JESP. Bug fix release of 2.3.2.
Release Date:
Wednesday, February 19, 2020Maven Central Repository には見つからなかった。
DatatypeConverterImpl クラスのソースコード。パッケージは javax.xml.bind となる。
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.gradledependencies { 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'
@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 に変更されている。
Change javax.* package name to jakarta.*
Release Date:
Friday, February 7, 2020Release 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.gradledependencies { 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 となる。
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(); }参考資料
- 投稿日:2020-02-15T10:26:47+09:00
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.javabuild.gradle
JAXB を使うため dependencies に javax.xml.bind:jaxb-api:2.3.1 を追加している。
build.gradleplugins { 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 executedDatatypeConverter.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(); }参考資料