Hi everyone! π
Today, I want to explore a powerful pattern in Uniface: building Distributed Applications using postmessage.
If you're coming from a modern stack like .NET (C#) or Java, Uniface might look a bit different. But don't worry! The concepts are surprisingly similar to what you already know. Think of Uniface's URouter as a built-in Message Broker (like RabbitMQ light) and postmessage as a way to fire events across different applications.
βΉοΈ Note: This tutorial uses Uniface 10.4. If you are using an older version, the concepts are the same, but some configuration details (like URouter paths) might differ slightly.
I've adapted a great example by Peter Beugel to help us understand how this works. Let's break it down step-by-step! π§
The Concept: Controller & Receiver ποΈ
Imagine two separate console apps running on your PC:
- The Controller (
CONTROLAPP): This is our Server or Monitor. It waits for tasks. - The Receiver (
TEST): This is our Client or Worker. It does the heavy lifting (like fetching data).
They don't talk directly. Instead, they shout messages to a "middleman" called the URouter (Uniface Router), which delivers them to the right place.
1. Configuration (The "App.Config") βοΈ
In Uniface 10.4, we use .asn (Assignment) files to configure the environment. It's just like appsettings.json or web.config.
We need to tell Uniface two things:
- Where is the Router? (The Connection String)
- Who am I? (The Identity)
controlapp.asn (Server Config):
[PATHS]
; $dnp = Default Network Path
; Connect to Router on localhost port 13001, identify as "THEADMIN"
$dnp=tcp:localhost+13001|||THEADMIN
userA.asn (Client Config):
[PATHS]
; Identify as "userA"
$dnp=tcp:localhost+13001|||userA
Addressing the Controller:
To send a message, we need an address. In Uniface, we use a Logical Name (like a DNS alias) in the config files so the code doesn't need hardcoded IPs.
[LOGICALS]
; "controller" points to: TCP, localhost:13001, OS User "MyUser", Router-ID "THEADMIN", Instance "CONTROLLER"
controller=TCP:localhost+13001|MyOsUser|||THEADMIN:CONTROLLER
2. The Code (The "Business Logic") π»
The Client (Receiver)
When this app starts, it wants to ask the Controller to do something. In C#, you might use HttpClient.PostAsync. In Uniface, we use postmessage.
operation exec
; Get the address from our config
$control$ = $logical("controller")
; Send a message: Target, MessageID, Data
; "Hey Controller, please trigger a 'RETRIEVE' for 'Andorra'!"
postmessage $control$ , "RETRIEVE" , "Andorra"
edit ; Keeps the window open
The Client also needs to listen for the answer. This is done in a Trigger (think of it as an Event Handler).
trigger receiveMessage
; Check the Command ID ($msgid)
selectcase $uppercase($msgid)
case "RETRIEVE"
; -- Database Logic starts here --
; "Entity" = Table. "Occurrence" = Row.
; If the table container is empty, fetch data.
if ($empty(COUNTRY)=1)
retrieve/e "COUNTRY"
endif
; Send "OK" back to whoever sent this message ($msginfo("SRC"))
postmessage $msginfo("SRC") , "OK", "Done!"
endselectcase
The Server (Controller)
The Controller in this example is a manual tester. It receives a message but waits for YOU (the human) to approve it by clicking a button.
Button Script:
; 'msrc', 'mid', 'mdata' are fields on the screen showing the last message.
; We just echo the exact same message back to the sender.
postmessage msrc , mid , mdata
3. The Flow: How it actually runs π
- Start Controller: It connects to the Router and waits. π§
- Start Receiver: It sends a "RETRIEVE" request to the Controller. π¨
- Controller Updates: The Controller screen shows: "Receiver wants 'RETRIEVE' for 'Andorra'".
- Human Action: You click the button on the Controller. "Okay, approved!" π±οΈ
- Echo Back: The Controller sends the command back to the Receiver.
- Execution: The Receiver gets its own command back, fetches the data from the DB, and reports "OK". β
Why use this? π€
This "Async Messaging" pattern is crucial for decoupled systems.
- Scalability: You can have 1 Controller and 50 Receivers.
- Responsiveness: The UI doesn't freeze while waiting for a long database query (because it's async!).
- Resilience: If the Receiver crashes, the Controller is still alive.
This is a classic pattern to learn the basics of Uniface's powerful middleware. Special thanks to Peter Beugel for the original code!
π Original PostMessage Example on Rocket Community
Happy Coding! π
Top comments (0)