Uso del servidor de base de datos H2 para notificar cambios a clientes (mensajería JMS)

Estoy utilizando con éxito la base de datos H2 en el modo AUTO_SERVER para que un archivo de base de datos se comparta entre varios clientes de escritorio en una red de forma transparente. De esta manera, se elige un servidor entre los clientes y todos los demás clientes leen desde el servidor tcp.

Lo que me falta es cómo un cliente o el servidor pueden notificar a todos los demás clientes de escritorio que algo ha cambiado en la base de datos. En este momento estoy usando un canal de JGroups para permitir que todos los clientes se comuniquen entre ellos, sin embargo, este es otro punto de falla y otro algoritmo de elección de líderes que se ejecuta en paralelo con el H2.

¿No hay otro método? He leído sobre el JMS (Java Message Service Java API) que se admite en algunas bases de datos. ¿Alguna pista para H2?

Gracias

EDITAR:

El siguiente código es una adaptación de la respuesta actual, si inicio el remitente primero (establecer args como "remitente") se conecta como servidor a la base de datos H2, luego ejecuto Receiver (configuro args como "receptor") en máquinas remotas y se conectan como clientes.

Sin embargo, solo el servidor recibe notificaciones, los clientes no reciben nada.

Esto tiene sentido por lo que sé actualmente: un activador solo se llama en el servidor, una función definida por el usuario llamada desde un cliente o servidor se llama en el cliente o servidor pero no en todos los clientes (y servidor) conectados a la base de datos.

Entonces, ¿hay una manera de adaptar lo siguiente para notificar a todas las instancias conectadas de un cambio en la base de datos?

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();
            }
        }
    }
}

Respuestas a la pregunta(1)

Su respuesta a la pregunta