[Java] 94 classファイルのJDKバージョン確認(逆コンパイル)

VSCodeが親切にも自動コンパイルして生成してくれたclassファイル。ビルドして得られたjarファイルが実行できないという事態が発生しました。
エラー内容はJava.lang.UnsupportedClassVersionErrorです。

VSCodeが使っているJDKのバージョンを確認しようとコマンドパレットからConfigure Java Runtimeを見ると設定画面がクラッシュしていました。

仕方ないので以下コマンドでclassファイルを逆コンパイルし、コンパイラのJDKバージョンを調べました。

javap -v classファイル名 | grep "version"
--------------------------------------------------
出力
--------------------------------------------------
minor version: 65535
major version: 61

一方、問題なくビルドできたclassファイルの逆コンパイル結果は以下の通りです。

minor version: 0
major version: 61

どちらもメジャーバージョンは17 (61マイナス44)で同じでしたが、マイナーバージョンが違っていました。

Macに入れているJDKの内容からVSCodeが使っているJDKはEclipse Temurin 17であることが判明しました。
*2022/1/30追記 ver 17.65535はTemurin版ではなくプレビュー機能で作成されたclassファイルのバージョン番号でした。つまりbinフォルダに自動生成されるclassファイルは元から正常なビルドができないということになります。確認すると実行コマンドのオプションが”–enable-preview”になっていました。テストで作成されたclassファイルを誤ってビルドに使わないための機能と理解しました。

VSCodeのConfigure Java Runtime設定がクラッシュしていなければ、もっと早く原因を突き止められたでしょう。

# MacにインストールしたJDKのリスト出力
/usr/libexec/java_home -V
--------------------------------------------------
出力
--------------------------------------------------
Matching Java Virtual Machines (4):
    17.0.1 (arm64) "Oracle Corporation" - "Java SE 17.0.1" /Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home
    17.0.1 (x86_64) "Eclipse Temurin" - "Eclipse Temurin 17" /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
    1.8.321.07 (x86_64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
    1.8.0_302 (x86_64) "Eclipse Temurin" - "Eclipse Temurin 8" /Library/Java/JavaVirtualMachines/temurin-8.jdk/Contents/Home

最近VSCodeには変数やメソッドのスコープ(修飾子)やライブラリのimportだけチェックしてもらい、コンパイルから先はターミナルで操作しています。拡張機能によるコンパイル&ビルド&実行は思う様に動いてくれないことが多々あるため使わなくなりました。

[Java] 93 カスタムJREの作成

カスタムJREをjar実行ファイルにバンドルすることで自製アプリのサイズが3分の1になりました。

# jar実行ファイルの依存モジュールを調べる
jdeps --list-deps test.jar

# カスタムJREの作成コマンド例
jlink --compress=2 \
    --module-path "/Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home/jmods" \
    --add-modules java.base,java.datatransfer,java.desktop,java.xml \
    --output [ランタイムイメージの出力先フォルダパス] \
    --strip-debug \
    --no-header-files

[Java] 92 Swing, JavaFXの比較

GUIアプリのSwingからJavaFXへの移植に際しJavaFXに問題があることが発覚したため、今後の方向性を検討するための資料として表にまとめました。

今のところはSwingに戻ってLook & Feelを自製するつもりです。ボタンの角を丸めたいだけなので比較的簡単にできると思います。あと原因がわからないのですが、JavaFXの精細性が低いのは意外でした。

Swingコンポーネントを埋め込むSwingFXは起動がかなり遅くなったので却下です。なおSwingFXはOracleのサイトで例示されていたクラス名です。ライブラリではないのでご注意ください。

格子状に並べたボタンの拡大画像
上:Swing 下:JavaFX

[Java] 91 JavaFX Borderの線種

JavaFXではボタンの枠線を二重線にできないことが発覚しました。実線、点線、破線のみです。ホバリングの際に二重線を出現させたかったのですががっかりです。線の表現力はSwingの方が数段上のようです。あとボタンの高さをどうしても変えられません。

5日間苦闘しながらJavaFXを再学習してきましたが、どうやら休止せざるを得なくなりました。グラデーションの掛かった図形を作ったりしたのに残念です。JavaScriptやWordPressの時もそうでしたが、CSS内の優先順位が見えなくて思う様に設定できずいつも苦労します。

またSwingに戻ってGUIアプリの作り込みに取り組んでいきます。問題の見た目はデフォルトがダサいだけで、手を入れればそれなりになると思います。

2022/1/18追記
枠線を二重線にできないのはSwingも同じ様です。SwingのL&F Metalではおそらくinsetsを設定して外側に複数の線を描いています。
JavaFXへのSwingコンポーネント埋め込みが可能な様なのでもう少し頑張ってみます。

[Java] 90 JavaFX Paneの詳細位置設定

TabヘッダやTabコンテンツの位置設定等に苦労していましたが、ようやく慣れてきました。この手の設定はSwingの方が断然楽です。

コードから設定する場合はPaneのsetStyleメソッド、CSSから設定する場合は各Paneのpaddingを調整します。ネット情報でコードからはCSSの内容をいじれないと書いていることがありますが、そんなことはありません。

特にTabPaneのタブの位置を調整するのに手こずりました。GitHubで配布しているcaspitan.cssやmodena.cssといった代表的なスタイルシートを元に自分なりにアレンジしました。

TabPaneの場合は以下の様に設定してタブをほぼ中央にもってきました。こういうのは私の様なCSS初心者が一から書いているとなかなか思いつかないです。”:top”を付けられずに立ち往生したでしょう。ネット情報では最後の砦のStackOverflow英語版でも類似の質問に対してまともな回答がありませんでした。今回はcaspitan.cssのおかげでなんとかできました。

CSSを使いこなすには書式を知っているか否かが全てでロジカルにアプローチできないのが厄介です。JavaFXが標準ライブラリから外されてしまった理由が何となく分かる様な気がします。

.tab-pane:top > .tab-header-area {
    -fx-background-insets: 0, 0 0 1 0;
    -fx-padding: 2 80 0 80 ;
}

[Java] 89 JavaFX TabPaneの設定

SwingアプリのJavaFX版作成に取り組んでいます。FXMLファイルは使いません。

CSSで細かい設定ができるので、Swingの見た目の野暮ったさは解消できそうです。

複数のタブを作成する際にそれぞれのタブのインスタンスを配列にしてみました。またタブ名のところに色付きの長方形を入れています。

参考までにコードを記しておきます。

<一部を抜粋>
TabPane leftpane = new TabPane();

Tab[] tabs = {new Tab0(),new Tab1(),new Tab2(),new Tab3(),new Tab4(),new Tab5()};
String[] title = {"Tab0","Tab1","Tab2","Tab3","Tab4","Tab5"};
Color[] colors = {null,Color.RED,Color.YELLOW,Color.BLUE,Color.BLACK};

for (int i = 0; i < 6; i++) {
	Tab tab = tabs[i];
	if (i >=1 && i<= 4){
		Rectangle r = new Rectangle();
			r.setWidth(20);
			r.setHeight(20);
			r.setArcWidth(10);
			r.setArcHeight(10);
			r.setFill(colors[i]);
			r.setOpacity(0.7);
		tab.setGraphic(r);
		tab.setText(title[i]);
	}else{
		tab.setText(title[i]);
	}
	leftpane.getTabs().add(tab);
}

以下、各タブのクラスを記述

[Java] 88 Apple公証方法の概要

[macOS Big Sur 11.6 , Mac mini M1 , Mac mini Intel]

最近開発したアプリをApp Storeに登録しようとしましたが、個人開発の場合は実名をApp Storeで公開することになるので、公証だけしてもらい自分のサイトにアップしました(署名するため結局公開になります)。

Appleの公証をパスできるようになるまでおよそ2日半掛かりました。成功するまでに”Your Mac software was not notarized.”のメールを38通受信しています。過去最高と言っていいくらいの壁でしたが、粘り強く食い下がって何とか解決できました。

Xcodeで開発した場合はIDE内で簡単に公証を受けられる様ですが、今回はJavaなのでほとんどターミナルでのコマンド処理でした。

流れは以下の通りです。あくまで私のケースでの進め方ですから参考程度にしていただければと思います。

1. Developer ID Applicationの証明書を発行する。
2. jar実行ファイルに署名する。
3. jpackageコマンドでappファイルを作成(引数は–type app-image他)して署名する。
4. appファイルをzipファイルに圧縮する。
5. zipファイルをxcrun altoolコマンドでAppleに提出する。
6. 1回で公証はまず通らない。ログを読んで署名ができていない(“The signature of the binary is invalid.”)と指摘されているファイルにappファイルからアクセスして署名する。
7. 追加署名したらzipファイルに圧縮して再提出。これを何回か繰り返す。
8. 署名漏れを完全にクリアすると公証が通った。
9. appファイルにステープラーでチケットを紐づける。
10. zipファイルに圧縮して自サイトにアップロードする。

dmgファイルで提出して署名漏れを指摘されても該当ファイルにアクセスできないのでappファイルにするのがポイントです。appファイルであれば中のContentsにアクセスして追加署名が可能です。

2回目以降は署名が必要なファイルが分かっているので提出前に全て署名しておけば一発で通ります。entitlements.plistの内容によるのかもしれませんが、私の場合はlibjli.dylibにも署名が必要でした。

なお公証を受けたファイルから署名の内容を確認できるため、結局実名は公開することになります。

[Java] 87 Swingコンポーネントの形状変更 Look&Feel

結構使いそうなのでメモ書きしておきます。

import javax.swing.UIManager;

void setLAF() {
		try {
			System.out.println(UIManager.getSystemLookAndFeelClassName());
			// UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
			UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
			// javax.swing.plaf.metal.MetalLookAndFeel
			// com.sun.java.swing.plaf.gtk.GTKLookAndFeel
			// com.sun.java.swing.plaf.motif.MotifLookAndFeel
			// com.sun.java.swing.plaf.windows.WindowsLookAndFeel
		} catch (Exception e) {
			e.printStackTrace();
		}
}

[Python] 322 ファイルの文字コードを一括変換

[Mac mini M1(2020), macOS Big Sur 11.6.1, Python 3.10.0]

Javaに関する基本的な知識を整理するため、”オラクル認定資格教科書 Javaプログラマ Silver SE11″を購入しました。Amazonセール半額の2090円だったので思わずポチりました。

例によってサンプルコードの文字コードがShift-JISのため、Macで読めるようUTF-8に一括変換しました。

まずサンプルコードのフォルダをコピーしフォルダ名を変更。自製アプリでディレクトリ構造を残したままファイルを全消去しました。そして以下の一括変換コードを実行しました。

ちなみにファイルを消さずに上書きになるようnkfコマンドを実行すると0バイトの空ファイルになります。

UTF-8からShift-JISへ変換する場合のコマンドは”nkf -s”になります。nkfはコマンド”brew install nkf”で前もってインストールしておきます。

JavaでしたらPathオブジェクトを文字列に変換する必要があるのですが、Pythonは不要なのでとても楽ですね。動的型付けのいいところです。

昨年9月に書いた変換先のフォルダ作成を含めたコードをよりシンプルにしました。

import glob,subprocess

dir = "/JavaSilver_samplecode"
paths_sjis = glob.glob(dir + "/**/*.java", recursive=True)

print(paths_sjis)

# 変換ファイルパス作成(フォルダ名を変更しファイル名はそのまま)
paths_utf8 = list()
for path in paths_sjis:
    path_utf8 = path.replace("JavaSilver_samplecode","JavaSilver_samplecode_utf8")
    paths_utf8.append(path_utf8)
    
print(paths_utf8)

for a,b in zip(paths_sjis,paths_utf8):
    cmd = "nkf -w %s > %s" %(a,b)
    subprocess.call(cmd, shell=True)

[Java] 86 jar実行ファイルの作成

# ソースコードのディレクトリに移動する
cd /projects/FileRemover/src

# ソースコードをJavaバイトコードにコンパイルする
javac App.java

# jar実行ファイルを作成する
jar cvfm FileRemover.jar manifest.mf *.class

# manifest.mfの内容(必ず改行する)
Main-Class: App

# jarを実行(ダブルクリックも可)
java -jar FileRemover.jar