jPOS-EE BinLog module

jPOS-EE has a new binlog module, a general purpose binary log that can be used as a reliable event store.

The jPOS BinLog has the following features:

  • multiple readers and writers can be used from the same JVM
  • multiple readers and writers can be used from different JVMs

Here is a sample Writer:

File dir = new File("/tmp/binlog");
try (BinLogWriter bl = new BinLogWriter(dir)) {
    bl.add( ... ); // byte array
    bl.add( ... ); // byte array
    bl.add( ... ); // byte array

and a sample Reader:

File dir = new File("/tmp/binlog");
try (BinLogReader bl = new BinLogReader(dir)) {
    while (bl.hasNext()) {
        byte[] b =;
        // do something with the byte[]

The BinLogReader implements an Iterator<BinLog.Entry>. Each BinLog.Entry has two main methods:

  • BinLog.Rer ref()
  • byte[] get()

While iterating over a BinLog, it might make sense to persistently store its BinLog.Ref in order to be able to restart the iterator at a given point if required (this is useful if using the BinLog to implement a Store and Forward.

The BinLogReader has two constructors:

  • BinLogReader(File dir)
  • BinLogReader(File dir, BinLog.Ref ref)

the latter can be used to restart the iterator at a given reference point obtained from a previous run.

In addition to the standard hasNext() method required by the Iterator implementation, BinLogReader also has a hasNext(long millis) method that waits a given number of milliseconds once it reaches the end of the log, attempting to wait for a new entry to be available.

The goal behind the BinLog implementation is to have a future proof file format easy to read from any language, 10 years down the road. We found that the Mastercard simple IPM file format, that’s basically a two-byte message length followed by the message itself was suitable for that. The payload on each record can be ISO-8583 (like Mastercard), JSON, FSDMsg based, Protocol buffers or whatever format the user choose.

But that format isn’t crash proof. If a system crashes while a record is being written to disk, the file can get easily corrupted. So we picked some ideas from Square’s tape project that implements a highly crash proof on-disk persistent circular queue using a very small header. Tape is great and we encourage you to consider it instead of this binlog for some use cases, but we didn’t want a circular queue, we wanted a place to securely store events for audit or store and forward purposes, and we also wanted to be able to access the same binlog from multiple JVMs with access to the same file-system, so we had to write our own.

The on-disk file format looks like this:

256 bytes Header
... Data
... Data

Header format (256 bytes):
4 bytes header length
2 bytes version
2 bytes Status (00=open, 01=closed)
8 bytes Last element position
4 bytes this log number
4 bytes next log number
232 bytes reserved

4 bytes Data length
...     Data

Each record has a length prefix (four bytes in network byte order) followed by its data. The header has a fixed length of 256 bytes but we found useful to make it look like a regular record too by providing its length at the very beginning. An implementation in any language reading a jPOS binlog can just be programmed to skip the first record.

At any given time (usually at end of day), a process can request a cut-over by calling the BinLogWriter.cutover() method in that case, all writers and readers will close the current file and move to the next one (Readers can choose to not-follow to the next file, for example while producing daily extracts).

In order to achieve file crash resilience, each write does the following:

  • Lock the file
  • Write the record’s length and data
  • Sync to disc
  • Write the last element position to the header
  • Sync to disc
  • Unlock the file

In an MBP with SDRAM we’ve managed to achieve approximately 6000 writes per second. On an iMac with regular disk the numbers go down to approximately 1500 writes per second for regular ISO-8583 message lengths (500..1000 bytes per record).

Due to the fact that the header is small enough to fit in an operating system block, the second write where we place the last element position happens to be atomic. While this works OK for readers and writers reading the file from different JVMs, that’s not the case for readers and writers running on the same JVM, even if they use a different file descriptor to open the file, the operating system stack has early access to the header that under high concurrency can lead to garbage values, that’s the reason the code synchronizes on a mutex object at specific places.

Supporting CLI commands

The binlog CLI command is a subsystem that currently have two commands:

  • monitor (to visually monitor a binlog)
  • cutover (to force a cutover)
  • exit (builtin command)

binlog accepts a parameter with the binlog’s path, i.e: binlog /tmp/binlog

So a cutover can be triggered from cron using the following command:

q2 --command="binlog /tmp/binlog; cutover; exit; shutdown --force"

jPOS 2.0.10 has been released

jPOS 2.0.10 has been released, new development version is now 2.1.0-SNAPSHOT

This release upgrades JLine to 3.0.0, adds new CLI commands and subsystems, upgrades Gradle to 3.1.0, allows license to be read from external file and removes double-logging in PADChannel. The main reason for this small release is to flush small enhancements before we make some bigger changes to the TransactionManager with improved audit logging (those are already available in 2.1.0-SNAPSHOT). It also updates the copyright year to 2017 and changes the copyright owner to the new entity jPOS Software SRL.

Starting with this release we’ll move to semantic versioning, so next release is going to be 2.1.0 and requires a full clean build of your projects in order to make sure some of the API changes don’t break your system.

see ChangeLog for details.

Q2 User Interface (aka 'QI')

I’m happy to share some new stuff we have in jPOS-EE that we call “QI” (short for Q2 UI).

TL;DR: go to using user admin and password test in order to see what I’m talking about. If you like it, keep reading, otherwise please ignore this message.

QI uses the Vaadin framework that we found very useful for our company’s web development and graphics design skillset (basically no skills). It allows us to create nice reusable web/mobile-friendly applications from Java in a highly modular way.

If you monitor @jposcommits you might have noticed a few new jPOS-EE components, like:

  • qi-core
  • qi-eeuser
  • qi-sysconfig

(see jPOS-EE/modules)

The main goal of the project is to have a bunch of reusable little QI modules (for instance, qi-minigl is coming) that you can use to put together very easily to administrate your jPOS application.

As the rest of the Q2 system, it uses an XML configuration file that gives you a top level control of your particular application, including centralised role-based permissions to access the different parts of the system.

<xml-config name="QI" logger="Q2">
    <title>jPOS QI</title>
        <menu name="System" icon="COG" style="icon-cog" action="system" />
        <menu name="Accounting"  action="accounting" />

    <sidebar id="info">
        <section name="Info"/>
        <option name="Info" action="info"/>

    <sidebar id="system">
        <section name="System"/>
        <option name="Info" action="info"/>
        <option name="Log" action="log"/>
        <option name="Exception log" action="exceptions"/>
        <option name="Users" action="users"/>
        <option name="Roles" action="roles"/>
        <option name="Permissions" action="permissions"/>
        <option name="SysConfig" action="sysconfig"/>
        <option name="Audit Log" action="syslog"/>
        <option name="Revision History" action="revision_history"/>

There you can see the top level menu that triggers two different sidebars. When we hit for example ‘Log’ it brings us to the ‘log’ action that is defined like this:

<view route="log" class="org.jpos.qi.system.LogListenerView" perm="sysadmin" sidebar="system" >
    <property name="name" value="logger.Q2.buffered"/>
    <property name="entityName" value="log"/>

If you hit the ‘info’ action, you get a tabbed pane, the tabbed pane is defined like this:

<view route="info" class="org.jpos.qi.views.TabView" perm="login" sidebar="system">
    <view caption="About" class="org.jpos.qi.system.AboutView" perm="login"/>
    <view caption="Memory" route="memory" 
        class="org.jpos.qi.system.MemoryUsageView" perm="login"/>
    <view caption="Sysmon" route="sysmon" 
        class="org.jpos.qi.system.ObjectView" perm="sysadmin" repeat="1000"
     <object class="org.jpos.util.SystemMonitor"/>
    <view caption="Uptime" route="uptime" class="org.jpos.qi.system.ProcessView" perm="sysadmin" repeat="1000"
    <view caption="Date" route="date" class="org.jpos.qi.system.ObjectView" 
        perm="login" repeat="1000"
      <object class="java.util.Date"/>

I suggest you take a look at how a simple view is implemented, for example the About view:

public class AboutView extends VerticalLayout implements View {
    public AboutView() {
        Label html = new Label(Q2.getVersionString());
    public void enter(ViewChangeListener.ViewChangeEvent event) { }

Some others like ObjectView or ProcessView are interesting, ObjectView can view any object and refresh it regularly, that’s how the ‘Date’ tab is implemented with a very simple XML configuration:

<view caption="Date" route="date" class="org.jpos.qi.system.ObjectView" 
       perm="login" repeat="1000"
    <object class="java.util.Date"/>

The ObjectView implementation is very simple (see code here) but behind the scenes QI uses web sockets (courtesy of a pre-configured Atmosphere setup) to refresh it.

If you go see the log and you telnet to on port 9000 (that has an open XMLChannel with an auto-responder) you can see how smooth it is.

If you like this stuff you can clone the qitest project that demonstrate how to use QI in your own application.

Your feedback and contributions are greatly appreciated. If you really want to use this stuff, we’ll be happy to walk you through the setup and help with questions for undocumented features (we’ll be adding documentation to the jPOS-EE manual as time permits). The easiest way to do that is here on the list, or better yet, in the jPOS Slack (we are usually around from 1600 to 2000 GMT - see to get an invite)

jPOS 2.0.8 has been released

jPOS 2.0.8 has been released, new development version is now 2.0.9-SNAPSHOT

see ChangeLog for details.

This release includes the former jPOS-EE SSH module that can be enabled by calling Q2 with the --ssh switch.

This requires a file cfg/authorized_keys and defaults to port 2222. After starting the SSH service, one can:

ssh -p 2222 admin@localhost

and get a Q2 CLI prompt.

See q2 --help for details.


In 2.0.4 we’ve added a SpaceLogListener that can be used to write log output in an asynchronous way.

It works with a companion LoggerService that pull entries from the Space and actually log them.

The configuration is very simple, here is an example:


<logger name="Q2" class="org.jpos.q2.qbean.LoggerAdaptor">
  <log-listener class="org.jpos.util.SpaceLogListener">
    <property name="queue" value="logger.q2" />
    <property name="space" value="tspace:default" />
    <property name="frozen" value="true" />


(this is basically your old 00_logger.xml, synchronously writing to disk)

<logger name="DAEMON" class="org.jpos.q2.qbean.LoggerAdaptor">
    <log-listener class="org.jpos.util.DailyLogListener">
        <property name="window" value="86400" />
        <property name="prefix" value="log/q2" />
        <property name="suffix" value=".log"/>
        <property name="date-format" value="-yyyy-MM-dd-HH"/>
        <property name="compression-format" value="gzip"/> 


<logger-service class="org.jpos.q2.qbean.LoggerService" logger="DAEMON">
    <property name="queue" value="logger.q2" />
    <property name="space" value="tspace:default" />

The logger service pulls entries from logger.q2 space queue and uses the second logger as its output (we arbitrarily called it DAEMON).

The SpaceLogListener can send to the Space the original “Live” (so to speak) LogEvent, or create a Frozen one (see the frozen property). When set to true, log filters (i.e. the ProtectedLogFilter) has to be applied before the SpaceLogListener, otherwise, it won’t work on the frozen LogEvent.

A FrozenLogEvent is Serializable, so we are not limited to TSpace, one can use the ReplicatedSpace in order to implement a very simple central logging.

ReplicatedSpace - 10 years later

/by apr/

10 years ago I add an experimental ReplicatedSpace implementation to the jPOS-EE project. While it was fun to develop and test, it didn’t get too much traction (or so I thought).

It turned out that I’ve started to hear about several high-end production systems using it, so I migrated it from the old GoogleCode repo to the new Github one.

Want to give it a quick try? Here are the very simple instructions:

Clone de jPOS-Template into a test directory

git clone rspace && cd rspace

Edit build.gradle

Add the dependency compile "", your dependencies will look like this:

dependencies {
  compile ('org.jpos:jpos:2.0.+') {
    exclude(module: 'junit')
    exclude(module: 'hamcrest-core')
  testCompile 'junit:junit:4.8.2'
  compile ""

Install resources

Call gradle installResources

This will extract the ReplicatedSpace configuration from its jar and will place it in your src/dist directory

Remove the file src/dist/deploy/01_multi_instance_prevention.xml so that you can run multiple instances on the same machine

Build a distribution

gradle dist

This will create a build/distributions/rspace-2.0.0.tar.gz that you can extract in several boxes. For a quick local run, you can gradle installApp and cd build/install/rspace to have the same effect.

Run the system

Call bin/q2 and you’ll see something like this:

<log realm="" at="Thu Jun 04 15:18:17.406 UYT 2015" lifespan="1ms">
   [apr-19136|0] (1) [apr-19136]

If you run it again, or in a different box in the same LAN, you’ll see several view-accepted messages.

At this point, you can create a little script to perform some space operations, i.e:

Space sp = SpaceFactory.getSpace("rspace:rspace");

A nice way to run a script is to deploy something like this:

Add a file called say 90_script.xml to your deploy directory:

<script> server(6666); </script>

Then telnet localhost 6667 (please note the server says ‘6666’ but you connect to 6666+1, this is BeanShell stuff.

At the bsh% prompt, you can type your space operations.