Skip to main content

Request Listener(s)

A QServer can have one or more ISORequestListener instances, which are configured as follows:

<qserver logger="Q2" name="qserver">
...
<request-listener class="com.your.ISORequestListenerImplementation" />
<request-listener class="com.your.AnotherISORequestListenerImplementation" />
...
</qserver>

The ISORequestListener interface is straightforward:

public interface ISORequestListener {
boolean process(ISOSource source, ISOMsg m);
}

Its contract is simple: the first listener that returns true—indicating it has handled the request—stops the server from invoking additional listeners down the chain.

Imagine you want to handle 2800 network management messages and reply to them immediately, without any further processing.

You can create a participant, such as org.jpos.tutorial.Handle2800, which might look like this:

package org.jpos.tutorial;

import org.jpos.iso.*;
import org.jpos.util.*;

public class Handle2800 implements ISORequestListener extends Log {
@Override
public boolean process(ISOSource source, ISOMsg m) {
try {
if ("2800".equals(m.getMTI())) {
ISOMsg r = (ISOMsg) m.clone();
r.setResponseMTI();
r.set(39, "0000");
source.send(r);
return true;
}
} catch (ISOException | IOException e) {
warn(e);
}
return false;
}
}

Save this snippet as src/main/java/org/jpos/tutorial/Handle2800.java, and then modify src/dist/deploy/50_qserver.xml to include the following configuration:

<qserver logger="Q2" name="qserver">
...
...
<request-listener class="org.jpos.tutorial.Handle2800" logger="Q2" />
...
...
</qserver>

Finally, run the following commands to deploy and start the server:

gradle installApp && q2

Once the server is running, send a 2800 network management message. You should receive a 2810 network management response in return.

extending Log

While extending org.jpos.util.Log is not strictly required, it can be useful in simple classes like this one that do not already extend another class. By inheriting from Log, you gain access to the logger subsystem without needing to implement LogSource. This allows you to use convenient methods like info, warn, error, and trace directly. For example, in the catch clause above, we use warn(e) to log exceptions.

Synchronous call

The QServer invokes the configured request listeners one by one, in a synchronous manner, until the first listener returns true, as described above. The fact that this is a synchronous call is very important because, for every accepted socket, QServer runs a single VirtualThread, and that thread is the one calling each request listener.

This means the request listener’s process method is expected to return quickly.

Providing a fast response, like in the example above for handling network management messages, is fine. Updating internal metrics or performing quick computations to enhance or modify the message is also acceptable (though there are ISOFilters better suited for such tasks). However, a request listener should never block the calling thread for a significant period of time.

In systems like jCard and jPTS, we configure most servers using the IncomingListener, which has a very simple process implementation:

public boolean process (ISOSource src, ISOMsg m) {
final Context ctx = new Context ();
ctx.put (timestamp, new Date(), remote);
ctx.put (source, src, remote);
ctx.put (request, m, remote);
if (additionalContextEntries != null) {
additionalContextEntries.entrySet().forEach(
e -> ctx.put(e.getKey(), e.getValue(), remote)
);
}
sp.out(queue, ctx, timeout);
return true;
}

This simple request listener implementation creates a new transaction Context, populates it with the ISOSource and ISOMsg request parameters, and sends it to the TransactionManager for further processing. The TransactionManager then handles sessions, timeouts, and, because it has access to the underlying ISOChannel (via the ISOSource parameter), it can send a response. Later in this documentation, you’ll see that we provide standard transaction participants, such as the SendResponse participant, specifically designed to handle responses.

note

QServer is not the only component that accepts ISORequestListeners. QMUX (the multiplexer) also uses them to handle unexpected transactions, such as late responses or requests originating from a link that acts as a server from a TCP/IP perspective.

As with all components, the request listeners can be Configurable, XMLConfigurable, and accept properties as most Q2 components. A common (sample) configuration would be:

  <request-listener class="org.jpos.iso.IncomingListener" logger="Q2">
<property name="queue" value="TXNMGR" />
<property name="ctx.DESTINATION" value="upstream-acquirer" />
</request-listener>
tip

In this particular case, IncomingListener adds all properties with the prefix ctx. to the Context, allowing them to be handled by custom transaction participants. For example, the property ctx.DESTINATION will be added to the newly created Context under the key DESTINATION.