JDK

Java 20

Java 20がリリースされました。今回は小規模な変更に留まっているようです。まったく新しく導入された機能はScoped Valuesのみで,他は2回目以降のインキュベーターまたはプレビューとしての更新です。

  • Scoped Values (JEP-429)
  • レコードパターン (JEP-432)
  • switch文のパターンマッチ (JEP-433)
  • 外部メモリ/ファンクションAPI (JEP-434)
  • 仮想スレッド (JEP-436) 
  • 構造化同時実行 (JEP-437)

Scoped Values

大規模プログラムにおいて,コンポーネント間で安全かつ効率的にデータを共有するための新たなメカニズムのようです。ScopedValue<T>というクラスが導入されています。Javaでは変数のスコープはクラスやメソッド,ブロックなどで定義される静的なスコープでしたが,今回導入されたスコープは動的なものです。

以下の例ではScopedValue<>型のstatic変数PRINCIPALを用いて,ServerクラスからDBAccessクラスへデータを共有させています。Serverクラスの(1)の部分で作成されたオブジェクトをDBAccesクラスの(3)の部分で読み取ることができるようになるというもののようです。Webサーバへアクセスしてきたユーザにデータベース接続権限がなかった場合は例外をスローするということが,メソッド呼出の際に権限情報を渡さずとも実現できるということでしょうか・・・。

class Server {
    final static ScopedValue<Principal> PRINCIPAL = new ScopedValue<>();   // (1)

    void serve(Request request, Response response) {
        var level     = (request.isAdmin() ? ADMIN : GUEST);
        var principal = new Principal(level);
        ScopedValue.where(PRINCIPAL, principal)                            // (2)
                   .run(() -> Application.handle(request, response));
    }
}

class DBAccess {
    DBConnection open() {
        var principal = Server.PRINCIPAL.get();                            // (3)
        if (!principal.canOpen()) throw new  InvalidPrincipalException();
        return newConnection(...);
    }
}

https://openjdk.org/jeps/429 より引用

雑記

SQL Server アップグレード

SQL Serverを2019から2022へアップグレードしました。アップグレードにあたってデータをエクスポートしてインポートするといった面倒な作業は一切不要というのがありがたいです。OSS製品の場合は互換性で苦労することもよくありますが,さすが商用製品です。

https://www.microsoft.com/ja-jp/sql-server/sql-server-2022

JDK

Java 19新機能

Java 19が公開されました。Java 19のLinux/RISC-V Port(JEP422)を除くとすべてPreviewかIncubatorとなっています。

  • レコードパターン(プレビュー)[JEP405]
  • Linux/RISC-Vポート[JEP422]
  • 外部関数と外部メモリAPI (プレビュー)[JEP424]
  • 仮想スレッド(プレビュー)[JEP425]
  • ベクターAPI(4次インキュベーター)[JEP426]
  • switch文パターンマッチ(3次プレビュー)[JEP427]
  • 構造化並列API(インキュベーター)[JEP428]

https://openjdk.org/projects/jdk/19/

JEP405: レコードパターン(プレビュー)

instanceof演算子の拡張みたいなものでしょうか。if ( obj instanceof A a)という書き方をして,objがクラスAのインスタンスであった場合はキャストされたオブジェクトが変数aに格納されるという記述ができました。

public class RecordPattern
{
    public static void main(String[] args)
    {
        Object obj = new String("Hello World");
        if(obj instanceof String str)
        {
            System.out.println(str);
        }
        else
        {
            System.out.println("It's not a instance of String");
        }
    }
}

Hello World

JEP424: 外部関数と外部メモリ(プレビュー)

java.lang.foreignパッケージに外部関数と外部メモリを扱うクラス等が用意されています。C言語の文字列長を検査するstrlen()をJavaから呼び出してみます。コンパイルには–enable-previewと -source 19という2つのオプションが必要です。

import java.lang.foreign.Linker;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.MemorySession;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;

public class ForeignFunction
{
    public static void main(String[] args) throws Throwable
    {
        Linker linker = Linker.nativeLinker();
        SymbolLookup stdlib = linker.defaultLookup();
        FunctionDescriptor functionDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
        MethodHandle strlen = linker.downcallHandle(stdlib.lookup("strlen").get(), functionDescriptor);
        String javaStrings   = "Hello from Java 19";
        long len = -1L;
        try(MemorySession session = MemorySession.openConfined())
        {
            MemorySegment cString = MemorySegment.allocateNative(javaStrings.length() + 1, session);
            cString.setUtf8String(0, javaStrings);
            len = (long)strlen.invoke(cString);
        }
        System.out.printf("strlen()=%d\n", len);
    }
}

ノート:ForeignFunction.javaはJava SE 19のプレビュー機能を使用します。
ノート:詳細は、-Xlint:previewオプションを指定して再コンパイルしてください。
strlen()=18

JEP425: 仮想スレッド(プレビュー)

従来のプラットフォームによるスレッドに加えて,Java VMによる仮想スレッドが登場しています。性能が著しく向上しているとのことですが,パフォーマンスまでは確認できていません。java.util.concurrent.ExecutorsクラスにnewVirtualThreadPerTaskExecutor()というstaticメソッドが追加されており,これを使って仮想スレッドを生成することができます。

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.stream.IntStream;

public class VirtualTrhead
{
    public static void main(String[] args) throws Exception
    {
        try(ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor())
        {
            IntStream.range(0, 10).forEach(i -> { executor.submit(new ExampleTask(i)); });
        }
    }
    static class ExampleTask implements Callable<Integer>
    {
        private int number;
        public ExampleTask(int arg)
        {
            number = arg;
        }
        @Override
        public Integer call() throws Exception
        {
            IntStream.range(0, 10).forEach(i -> System.out.printf("Hello %d\n", number));
            return number;
        }
    }
}

JEP426: ベクターAPI(インキュベーター)

ベクトル計算が容易に記述できるようになります。ベクトルAとベクトルBが存在するとして,(A×A+B×B)×(-1)の計算を行う例です。配列を使った場合はループを用いて計算をくり返す必要があります。ベクターAPIでGPUを用いた最適化が行われるような環境では高いパフォーマンスを期待できるのでしょうか・・・。

次のようにコマンドを実行してコンパイルしています。

javac –enable-preview -source 19 –add-modules jdk.incubator.vector

import java.util.Arrays;
import jdk.incubator.vector.DoubleVector;
import jdk.incubator.vector.VectorMask;
import jdk.incubator.vector.VectorSpecies;

public class VectorAPI
{
    private static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;
    public static void main(String[] args)
    {
        VectorAPI instance = new VectorAPI();
        double[] array = new double[] { 1.0d, 2.0d, 3.0d };
        double[] result = new double[array.length];
        instance.calculate_by_vector(array, array, result);
        Arrays.stream(result).forEach(f -> System.out.print(f));
    }
    private void calculate_by_vector(double[] array1, double[] array2, double[] result)
    {
        for(int i = 0; i < array1.length; i+= SPECIES.length())
        {
            VectorMask<Double> mask = SPECIES.indexInRange(i, array1.length);
            DoubleVector vector1 = DoubleVector.fromArray(SPECIES, array1, i, mask);
            DoubleVector vector2 = DoubleVector.fromArray(SPECIES, array2, i, mask);
            DoubleVector resultVector = vector1.mul(vector1).add(vector2.mul(vector2)).neg();
            resultVector.intoArray(result, i, mask);
        }
    }
}

警告:実験的なモジュールを使用しています: jdk.incubator.vector
警告1個
WARNING: Using incubator modules: jdk.incubator.vector
-2.0 -8.0 -18.0

JEP427: switch文パターンマッチ(プレビュー)

switch文は更に機能拡張されるようです。データ型による分岐に加えてその値を評価する機能が追加されています。複雑になりすぎて使いこなせない,あるいは,バグが紛れ込みやすくなってしまわないか心配されます。

import java.util.ArrayList;
import java.util.List;

public class SwitchPatternMatch
{
    public static void main(String[] args)
    {
        List<Object> list = makeList();
        for(Object obj : list)
        {
            System.out.printf("%s\t", (obj == null) ? "null" : obj.getClass().getName());
            switch(obj)
            {
                case null -> System.out.println("[A] null");
                case Integer i when i < 5 -> System.out.printf("[B] (%d)\n", i);
                case Integer i -> System.out.printf("[C] (%d)\n", i);
                case String s -> System.out.printf("[D] (\"%s\")\n", s);
                default -> System.out.printf("[E] (%s) \n", obj.toString());
            }
        }
    }
    private static List<Object> makeList()
    {
        List<Object> list = new ArrayList<>();
        list.add(null);
        list.add(0);
        list.add(1);
        list.add(10);
        list.add(20d);
        list.add(new String("Hello"));
        list.add(Boolean.TRUE);
        list.add(list);
        return list;
    }
}

ノート:SwitchPatternMatch.javaはJava SE 19のプレビュー機能を使用します。
ノート:詳細は、-Xlint:previewオプションを指定して再コンパイルしてください。
null [A] null
java.lang.Integer [B] (0)
java.lang.Integer [B] (1)
java.lang.Integer [C] (10)
java.lang.Double [E] (20.0)
java.lang.String [D] (“Hello”)
java.lang.Boolean [E] (true)
java.util.ArrayList [E] ([null, 0, 1, 10, 20.0, Hello, true, (this Collection)])

JEP428: 構造化並列API(インキュベーター)

マルチスレッドで動作させる場合に子スレッドを効果的に管理する仕組みのようである。StructuredTaskScopeというクラスが導入されており,子スレッドのうち1つでも失敗していた場合は全体を失敗として例外をスローするなどが行えます。

import java.util.concurrent.Callable;
import jdk.incubator.concurrent.StructuredTaskScope;
import jdk.incubator.concurrent.StructuredTaskScope.ShutdownOnSuccess;
import jdk.incubator.concurrent.StructuredTaskScope.ShutdownOnFailure;

public class StructuredTaskAPI
{
    public static void main(String[] args) throws Exception
    {
        successExample();
        try
        {
            failExample();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
    private static void successExample() throws InterruptedException
    {
        try(StructuredTaskScope.ShutdownOnSuccess<String> scope = new StructuredTaskScope.ShutdownOnSuccess<>())
        {
            scope.fork(() -> { return "Hello World";});
            scope.fork(() -> { return "Bye";});
            scope.join();
            String result = scope.result(e -> new RuntimeException("発生しないはず"));
            System.out.println(result);
        }
    }
    private static void failExample() throws Exception
    {
        try(StructuredTaskScope.ShutdownOnFailure scope = new StructuredTaskScope.ShutdownOnFailure())
        {
            scope.fork(new Callable<String>() {
                @Override
                public String call() throws InterruptedException
                { 
                    Thread.sleep(30); 
                    return "Hello World";
                }
            });
            scope.fork(new Callable<Integer>() {
                @Override
                public Integer call() throws InterruptedException
                {
                    throw new RuntimeException("ダミーのエラー");
                }
            });
            scope.join();
            scope.throwIfFailed();
        }
    }
}

警告:実験的なモジュールを使用しています: jdk.incubator.concurrent,jdk.incubator.vector
警告1個
WARNING: Using incubator modules: jdk.incubator.vector, jdk.incubator.concurrent
Hello World
java.util.concurrent.ExecutionException: java.lang.RuntimeException: ダミーのエラー
at jdk.incubator.concurrent/jdk.incubator.concurrent.StructuredTaskScope$ShutdownOnFailure.throwIfFailed(StructuredTaskScope.java:1125)
at StructuredTaskAPI.failExample(StructuredTaskAPI.java:51)
at StructuredTaskAPI.main(StructuredTaskAPI.java:13)
Caused by: java.lang.RuntimeException: ダミーのエラー
at StructuredTaskAPI$2.call(StructuredTaskAPI.java:47)
at StructuredTaskAPI$2.call(StructuredTaskAPI.java:43)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.lang.VirtualThread.run(VirtualThread.java:287)
at java.base/java.lang.VirtualThread$VThreadContinuation.lambda$new$0(VirtualThread.java:174)
at java.base/jdk.internal.vm.Continuation.enter0(Continuation.java:327)
at java.base/jdk.internal.vm.Continuation.enter(Continuation.java:320)

新型コロナウイルス関連

ワクチンパスポート使えず・・・

12月20日にデジタル庁からワクチンパスポートアプリ(名称「新型コロナワクチン接種証明書アプリ」)が出されましたが,私のモバイル端末(Apple製 iPad Pro,HUAWEI製Nova lite 3)はいずれも非対応でした。仕方がないので,紙の証明書を区役所に申請しました。

https://www.digital.go.jp/policies/posts/vaccinecert

雑記

消費税の任意中間申告の誤指摘

本日,税務署から電話がありました。税務調査かと思い,一瞬緊張しましたが,消費税の任意の中間申告が期限から遅れて申告されていたので,納税された金額を還付すると言われました。とりあえず,税務署の職員さんがそう言うのだから納期に遅れてしまったのだろうと思い「わかりました」と応じて一旦電話を切りました。その後,国税庁のWebページで確認すると,任意の中間申告は対象期間が1月から6月で,申告は対象期間の末日の翌日から起算して2月以内ということでした。つまり,6月末の翌日である7月1日から起算して2月後の8月末まででした。税務署に電話をして確認すると,その通りということで,無事に受理して頂きました。

https://www.nta.go.jp/taxes/shiraberu/taxanswer/shohi/6611.htm

これで還付されてしまったら,国税不服審判所に不服申立をするところでした。

情報セキュリティ

個人/SOHOでも導入可能なLinux用セキュリティソフト

サーバーに限れば広く使われているLinux OSだが,個人やSOHOなどで導入可能なセキュリティソフトの選択肢はあまりない。大手のセキュリティソフトベンダもLinux向けの製品を出してはいるものの,大規模ユーザ向けのものが多く,小規模事業者や個人には手がだせない。

以下の製品は無料または,比較的安価であり,1台から導入可能で,個人でも導入できる。

ESET File Security for Linux

https://www.eset.com/jp/business/server-antivirus/file-security-linux/

ESETはスロバキアの企業が開発したセキュリティソフトである。キヤノンITソリューションズが代理店となっている。同社の価格表によると,新規ライセンスが年間20,000円,更新ライセンスが年間10,000円となっている。

価格表 https://eset-info.canon-its.jp/business/file_security/price.html

Dr.Web

Dr.Webはロシアの企業が開発したセキュリティソフトで,日本語でのサポート対応を行っている。年間3,410円で利用できる。

https://products.drweb.co.jp/home/linux/

Comodo Antivirus for Linux

Comodo Antivurus for LinuxはComodo社が開発したセキュリティソフトで,無料で使用できる。英語の情報に頼ることにはなるが,無料とは思えないクオリティである。

https://www.comodo.com/home/internet-security/antivirus-for-linux.php

ClamAV

ClamAVは無料で利用できる。メールサーバ用に開発されたもののようである。誤検知が多かった印象を持っている。公式の日本語情報はないため,英語のドキュメントを読む必要がある。

http://www.clamav.net/

情報セキュリティ

Linux版セキュリティ対策ソフトDr.Web導入

Linux版のセキュリティ対策ソフトDr.Webを導入してみました。ロシアの会社が開発したものですが,日本にも拠点があり,日本語でサポートを受けられます。価格も年間3410円と安価です。

https://www.drweb.co.jp/

[root]# drweb-ctl appinfo
ConfigD; 1349; RUNNING 1; Installed (ConfigD ScanEngine FileCheck Update ESAgent NetCheck StatD GateD MailD Antispam UrlCheck CloudD MeshD LinuxGUI LinuxSpider LinuxFirewall), Should run (Update), UUID: 52eef68d-78ad-4c72-8d57-d2cea838f819, created: 2021-Sep-04 21:31:31
Update; 2195; RUNNING 1


[root]# drweb-ctl log
2021-Sep-06 12:18:37 ConfigD [1349] Notice: License number 147896597, expires 2022-Sep-08 12:11:08
2021-Sep-06 12:18:37 Update [2195] Notice: Update 11.1.6.2012181656: Ready for service
2021-Sep-06 12:18:38 Update [2195] Notice: Downloaded vrpatch-56.kwo (38842 bytes)
2021-Sep-06 12:18:53 Update [2195] Notice: Downloaded vrpatch-57.kwo (35479 bytes)
2021-Sep-06 12:18:53 ConfigD [1349] Notice: Update: placed “/var/opt/drweb.com/antispam/vrpatch-56.kwo”
2021-Sep-06 12:18:53 ConfigD [1349] Notice: Update: placed “/var/opt/drweb.com/antispam/vrpatch-57.kwo”
2021-Sep-06 12:18:53 ConfigD [1349] Notice: Update: placed “/var/opt/drweb.com/drl/antispam/version2.lst”
2021-Sep-06 12:24:28 Update [2195] Notice: Downloaded vrpatch-58.kwo (35915 bytes)
2021-Sep-06 12:24:28 ConfigD [1349] Notice: Update: placed “/var/opt/drweb.com/antispam/vrpatch-58.kwo”
2021-Sep-06 12:24:28 ConfigD [1349] Notice: Update: placed “/var/opt/drweb.com/drl/antispam/version2.lst”

情報セキュリティ

セキュリティソフトの誤検知

セキュリティソフトのMcAfee製マカフィーリブセーフが,私が作成した暗号化アプリをマルウェアとしてブロックしてくれました。実行ファイルが隔離されてしまいました。中身もすべてわかっていますので,ビルドし直した上で再度インストールして,ホワイトリストに登録しました。幸い,一般に提供しているものではなかったので被害者は自分だけでした。