Używanie serwera bazy danych H2 do powiadamiania o zmianach klientów (przesyłanie wiadomości JMS)

Z powodzeniem używam bazy danych H2 w trybie AUTO_SERVER, aby plik bazy danych był współużytkowany przez wielu klientów komputerów stacjonarnych w sieci w sposób przezroczysty. W ten sposób serwer jest wybierany wśród klientów i wszystkich innych klientów odczytywanych z serwera tcp.

Brakuje mi sposobu, w jaki klient lub serwer może powiadomić wszystkie inne komputery klienckie o czymś, co zostało zmienione w bazie danych. Obecnie używam kanału JGroups, aby umożliwić wszystkim klientom komunikowanie się ze sobą, ale jest to kolejny punkt awarii i inny algorytm wyboru lidera, który działa równolegle z H2.

Czy nie ma innej metody? Przeczytałem o JMS (Java Message Service Java API), który jest obsługiwany w niektórych bazach danych. Jakaś podpowiedź do H2?

Dzięki

EDYTOWAĆ:

Poniższy kod jest adaptacją bieżącej odpowiedzi, jeśli uruchomię najpierw Sender (ustaw args jako „nadawca”), łączy się on jako serwer z bazą danych H2, a następnie wykonuję Receiver (ustaw args jako „odbiornik”) na zdalnych komputerach i łączą się jako klienci.

Jednak tylko serwer otrzymuje powiadomienia, klienci nic nie otrzymują.

Ma to sens z tego, co obecnie wiem: wyzwalacz jest wywoływany tylko na serwerze, funkcja klienta zdefiniowana z klienta lub serwera jest wywoływana na kliencie lub serwerze, ale nie na wszystkich klientach (i serwerze) połączonych z bazą danych.

Czy istnieje sposób na dostosowanie poniższego, aby powiadomić wszystkie połączone instancje o zmianie bazy danych?

import java.io.File;
import java.sql.*;
import java.util.concurrent.atomic.AtomicLong;
import org.h2.tools.TriggerAdapter;

public class TestSimpleDB2
{

    public static void main(String[] args) throws Exception
    {
        //final String url = "jdbc:h2:mem:test;multi_threaded=true";
        final String url = "jdbc:h2:" + File.separator + "mnt/testdir/PlanIGS" + File.separator
                + "persondb;create=true;AUTO_SERVER=TRUE;multi_threaded=true";
        Connection conn = DriverManager.getConnection(url);
        Statement stat = conn.createStatement();

        boolean isSender = false;
        args = new String[]
        {
            "sender"
        };
        for (String arg : args)
        {
            if (arg.contains("receiver"))
            {
                System.out.println("receiver starting");
                isSender = false;
            }
            else if (arg.contains("sender"))
            {
                System.out.println("sender starting");
                isSender = true;
            }
        }

        if (isSender)
        {
            stat.execute("create alias wait_for_change for \""
                    + TestSimpleDB2.class.getName()
                    + ".waitForChange\"");
            stat.execute("create table test(id identity)");
            stat.execute("create trigger notifier "
                    + "before insert, update, delete, rollback "
                    + "on test call \""
                    + TestSimpleDB2.Notifier.class.getName() + "\"");

            Thread.sleep(1000);
            for (int i = 0; i < 10; i++)
            {
                System.out.println("Sender: I change something...");
                stat.execute("insert into test values(null)");
                Thread.sleep(2000);
            }
        }
        else
        {
            new Thread()
            {
                public void run()
                {
                    try
                    {
                        Connection conn = DriverManager.getConnection(url);
                        for (int i = 0; i < 10; i++)
                        {
                            conn.createStatement().execute(
                                    "call wait_for_change(100000)");
                            System.out.println("Receiver: event received");
                        }
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
        conn.close();
    }

    static AtomicLong modCount = new AtomicLong();

    public static void waitForChange(long maxWaitMillis)
    {
        synchronized (modCount)
        {
            try
            {
                modCount.wait(maxWaitMillis);
            }
            catch (InterruptedException e)
            {
                // ignore
            }
        }
    }

    public static class Notifier extends TriggerAdapter
    {

        public void fire(Connection conn, ResultSet oldRow, ResultSet newRow)
                throws SQLException
        {
            modCount.incrementAndGet();
            synchronized (modCount)
            {
                modCount.notifyAll();
            }
        }
    }
}

questionAnswers(1)

yourAnswerToTheQuestion