Overview

IMPORTANT

This document was converted from thelinux setup guide using AI (look at my reasoning for using Linux to understand why I daily drive Linux) so commands it says might not be 100% accurate or fully functional.

The bahmni.appointment.forwarder plugin is an OpenMRS OMOD that intercepts Bahmni appointment events and forwards them to a central RabbitMQ broker. This guide covers setup on Windows using PowerShell and Docker Desktop.

All commands in this guide use PowerShell. Do not use Command Prompt (cmd.exe) as it does not support the syntax used here.


Prerequisites

Required software

SoftwareDownloadPurpose
Java JDK 21adoptium.netRunning OpenMRS and building the plugin
Maven 3.xmaven.apache.orgBuilding the plugin
Gitgit-scm.comCloning repositories
Docker Desktopdocker.comRunning OpenMRS locally
Python 3python.orgReading RabbitMQ messages during testing

Verify installations

Open PowerShell and run:

java -version
mvn -version
git --version
docker --version
docker compose version
python --version

All commands should return version information without errors.

Set JAVA_HOME

Maven requires JAVA_HOME to be set. In PowerShell:

[System.Environment]::SetEnvironmentVariable("JAVA_HOME", "C:\Program Files\Eclipse Adoptium\jdk-21.0.0.37-hotspot", "Machine")

Replace the path with your actual JDK installation path. Restart PowerShell after setting this.


Building the Dependencies

The plugin requires two OpenMRS modules to be built locally before the plugin itself can be built.

Build the event module

cd C:\Users\YourName\Repositories
git clone https://github.com/openmrs/openmrs-module-event.git
cd openmrs-module-event
mvn clean install -DskipTests

Build the appointments module

cd C:\Users\YourName\Repositories
git clone https://github.com/Bahmni/openmrs-module-appointments.git openmrs-module-appointments-2.1
cd openmrs-module-appointments-2.1
git checkout 2.1.0
mvn clean install -DskipTests

Both builds should end with BUILD SUCCESS.


Building the Plugin

cd AvansModuleOpenMRS\bahmni.appointment.forwarder
mvn clean install -DskipTests

The OMOD file is generated at:

omod\target\bahmni.appointment.forwarder-1.0.0-SNAPSHOT.omod

Deploying to OpenMRS

Step 1, Clone the OpenMRS distribution

cd C:\Users\YourName\Desktop
git clone https://github.com/openmrs/openmrs-distro-referenceapplication.git OpenMRSLocally
cd OpenMRSLocally

Step 2, Set up the modules folder

New-Item -ItemType Directory -Path modules -Force
Copy-Item "C:\Users\YourName\Repositories\AvansModuleOpenMRS\bahmni.appointment.forwarder\omod\target\bahmni.appointment.forwarder-1.0.0-SNAPSHOT.omod" -Destination "modules\"

Step 3, Update docker-compose.yml

Open docker-compose.yml in a text editor and find the volumes block under the backend service. Add the bind mount line:

volumes:
  - openmrs-data:/openmrs/data
  - ./modules/bahmni.appointment.forwarder-1.0.0-SNAPSHOT.omod:/openmrs/distribution/openmrs_modules/bahmni.appointment.forwarder-1.0.0-SNAPSHOT.omod

The bind mount uses forward slashes even on Windows. Docker Desktop handles the path conversion automatically. Do not copy the OMOD directly into /openmrs/data/modules/ as it will be wiped on every restart.

Step 4, Start OpenMRS

docker compose up -d

Wait approximately 3 minutes for OpenMRS to fully start. Monitor startup with:

docker logs -f (docker ps --filter name=backend -q) 2>$null

Or filter for relevant lines:

docker logs (docker ps --filter name=backend -q) 2>$null | Select-String "done refreshing|forwarder|started"

Once you see the following lines the plugin is loaded:

=== BAHMNI APPOINTMENT FORWARDER CONTEXT REFRESHED ===
=== BAHMNI APPOINTMENT FORWARDER STARTED ===

Configuring Global Properties

The plugin reads all its configuration from OpenMRS global properties. Set them directly in the database after the first startup.

First get the database container name:

docker ps --format "{{.Names}}"

Look for the container with db in its name, for example openmrs-distro-referenceapplication-main-db-1. Then run:

docker exec openmrs-distro-referenceapplication-main-db-1 `
  mysql -u openmrs -popenmrs openmrs -e "
INSERT INTO global_property (property, property_value, description, uuid) VALUES
('communicatie.tenantId',         'your-tenant-id',    'Tenant identifier',   UUID()),
('communicatie.rabbitmq.host',    'your-host',         'RabbitMQ host',       UUID()),
('communicatie.rabbitmq.port',    '5672',              'RabbitMQ port',       UUID()),
('communicatie.rabbitmq.username','your-username',     'RabbitMQ username',   UUID()),
('communicatie.rabbitmq.password','your-password',     'RabbitMQ password',   UUID())
ON DUPLICATE KEY UPDATE property_value=VALUES(property_value);" 2>$null

Replace the container name and placeholder values:

PropertyExampleDescription
communicatie.tenantIdhospital-amsterdamUnique ID for this OpenMRS instance
communicatie.rabbitmq.host86.48.5.113IP or hostname of the RabbitMQ server
communicatie.rabbitmq.port5672AMQP port
communicatie.rabbitmq.usernamecommunicatieRabbitMQ username
communicatie.rabbitmq.passwordyourpasswordRabbitMQ password

Verify the properties were saved:

docker exec openmrs-distro-referenceapplication-main-db-1 `
  mysql -u openmrs -popenmrs openmrs `
  -e "SELECT property, property_value FROM global_property WHERE property LIKE 'communicatie%';" 2>$null

Step 5, Restart to apply properties

docker compose restart backend

Wait for OpenMRS to start again. You should now see the RabbitMQ connection confirmed:

=== BAHMNI APPOINTMENT FORWARDER STARTED ===
=== CONNECTED TO RABBITMQ AT your-host:5672 ===

Verifying the Setup

Check the exchange exists

curl -s -u 'username:password' `
  'http://your-rabbitmq-host:15672/api/exchanges/%2F/appointment.exchange'

A JSON response confirms the plugin connected. A 404 means the plugin has not connected yet.

Create a test queue binding

curl -s -u 'username:password' `
  -X PUT 'http://your-rabbitmq-host:15672/api/queues/%2F/debug.queue' `
  -H 'Content-Type: application/json' `
  -d '{"durable":true,"auto_delete":false}'
 
curl -s -u 'username:password' `
  -X POST 'http://your-rabbitmq-host:15672/api/bindings/%2F/e/appointment.exchange/q/debug.queue' `
  -H 'Content-Type: application/json' `
  -d '{"routing_key":"appointment.#"}'

Create a test appointment

curl -s -u admin:Admin123 -X POST `
  'http://localhost/openmrs/ws/rest/v1/appointment' `
  -H 'Content-Type: application/json' `
  -d '{
    "patientUuid": "YOUR_PATIENT_UUID",
    "serviceUuid": "YOUR_SERVICE_UUID",
    "startDateTime": "2026-06-01T10:00:00.000Z",
    "endDateTime": "2026-06-01T10:30:00.000Z",
    "appointmentKind": "Scheduled",
    "status": "Scheduled"
  }'

Read the message from the queue

$response = curl -s -u 'username:password' `
  -X POST 'http://your-rabbitmq-host:15672/api/queues/%2F/debug.queue/get' `
  -H 'Content-Type: application/json' `
  -d '{"count":1,"ackmode":"ack_requeue_true","encoding":"auto"}'
 
$data = $response | ConvertFrom-Json
if ($data -is [array] -and $data.Count -gt 0) {
    $data[0].payload | ConvertFrom-Json | ConvertTo-Json -Depth 10
}

Redeploying After Code Changes

After rebuilding the plugin, run these commands to deploy the new OMOD:

# Copy new OMOD to modules folder
Copy-Item "C:\Users\YourName\Repositories\AvansModuleOpenMRS\bahmni.appointment.forwarder\omod\target\bahmni.appointment.forwarder-1.0.0-SNAPSHOT.omod" `
  -Destination "modules\" -Force
 
# Clear the OpenMRS lib cache for the plugin
$volumePath = docker inspect openmrs-distro-referenceapplication-main-backend-1 `
  --format='{{range .Mounts}}{{if eq .Destination "/openmrs/data"}}{{.Source}}{{end}}{{end}}'
Remove-Item -Recurse -Force "$volumePath\.openmrs-lib-cache\bahmni.appointment.forwarder" -ErrorAction SilentlyContinue
 
# Restart the backend
docker restart openmrs-distro-referenceapplication-main-backend-1

If the volume path command does not work, find the volume mount path manually in Docker Desktop under Volumes, or use docker inspect and look for the Mounts section.


Message Format

Every appointment create or reschedule publishes the following JSON payload to appointment.exchange:

{
  "tenantId":          "hospital-amsterdam",
  "eventType":         "BAHMNI_APPOINTMENT_CREATED",
  "timestamp":         "2026-05-20T13:28:00Z",
  "appointmentUuid":   "ad37be8a-227d-4486-8862-513dafb9fe77",
  "appointmentNumber": "0000",
  "appointmentKind":   "Scheduled",
  "patientUuid":       "c35a048b-c311-4f5d-8a21-322f469d16e3",
  "patientName":       "Richard Nelson",
  "patientIdentifier": "10001LL",
  "phoneNumber":       "+31612345678",
  "emailAddress":      "richard.nelson@test.com",
  "startDateTime":     "2026-05-31T10:00:00Z",
  "endDateTime":       "2026-05-31T10:30:00Z",
  "serviceName":       "General Medicine",
  "serviceUuid":       "3bd2766b-538d-11f1-a6ad-a2e877b8b22b",
  "status":            "Scheduled",
  "comments":          null,
  "voided":            false
}

Routing key format: appointment.{tenantId}.{eventType} Event types:

  • BAHMNI_APPOINTMENT_CREATED, fired when validateAndSave is called
  • BAHMNI_APPOINTMENT_UPDATED, fired when rescheduleAppointment is called

Troubleshooting

Docker Desktop not starting containers

Make sure WSL 2 is enabled. In PowerShell as Administrator:

wsl --install
wsl --set-default-version 2

Restart Docker Desktop after enabling WSL 2.

Port 80 already in use

If another application is using port 80, stop it before starting OpenMRS. To find what is using port 80:

netstat -ano | findstr ":80 "

Then stop the process with its PID:

Stop-Process -Id YOUR_PID -Force

Plugin not appearing in the module list

Check that the OMOD file is in the distribution folder inside the container:

docker exec openmrs-distro-referenceapplication-main-backend-1 `
  ls /openmrs/distribution/openmrs_modules/ | findstr forwarder

If it is missing, the bind mount is not working. In Docker Desktop, go to Settings and make sure your drive is shared under Resources and File Sharing.

Plugin appears but does not start

Check the logs:

docker logs openmrs-distro-referenceapplication-main-backend-1 2>$null `
  | Select-String "forwarder|bahmni.appointment.forwarder" `
  | Select-Object -Last 20

RabbitMQ not connecting

Verify the global properties are set:

docker exec openmrs-distro-referenceapplication-main-db-1 `
  mysql -u openmrs -popenmrs openmrs `
  -e "SELECT property, property_value FROM global_property WHERE property LIKE 'communicatie%';" 2>$null

Old OMOD cached after redeployment

Find the Docker volume data path in Docker Desktop under Volumes, then delete the cache folder for the plugin. Alternatively use Docker Desktop’s terminal to run:

docker exec openmrs-distro-referenceapplication-main-backend-1 `
  rm -rf /openmrs/data/.openmrs-lib-cache/bahmni.appointment.forwarder
docker restart openmrs-distro-referenceapplication-main-backend-1

mvn command not found

Maven needs to be on your system PATH. After installing Maven, add it manually:

$mavenPath = "C:\Program Files\Apache\maven-3.9.0\bin"
[System.Environment]::SetEnvironmentVariable("PATH", $env:PATH + ";$mavenPath", "Machine")

Replace the path with your actual Maven installation. Restart PowerShell after making this change.

git clone uses SSH but SSH key is not set up

Use HTTPS instead of SSH when cloning:

git clone https://github.com/openmrs/openmrs-module-event.git
git clone https://github.com/Bahmni/openmrs-module-appointments.git openmrs-module-appointments-2.1
git clone https://github.com/ChrisHorler/AvansModuleOpenMRS.git