DEV Community

Peter + AI
Peter + AI

Posted on

πŸš€ Mastering Asynchronous Messaging in Uniface: A Beginner-Friendly Guide Uniface 10.4

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:

  1. The Controller (CONTROLAPP): This is our Server or Monitor. It waits for tasks.
  2. 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 πŸ”„

  1. Start Controller: It connects to the Router and waits. 🧘
  2. Start Receiver: It sends a "RETRIEVE" request to the Controller. πŸ“¨
  3. Controller Updates: The Controller screen shows: "Receiver wants 'RETRIEVE' for 'Andorra'".
  4. Human Action: You click the button on the Controller. "Okay, approved!" πŸ–±οΈ
  5. Echo Back: The Controller sends the command back to the Receiver.
  6. 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)