Skip to main content

Directory Structure

jPOS applications have the following directory structure:

  • a main jar containing the application classes that goes to the top level directory.
  • a 'lib' directory containing dependencies.
  • a 'deploy' directory containing the service definitions (QBeans).
  • a 'cfg' directory with optional configuration files.

Here is an example:

|── your-jpos-app.jar
├── bin
   ├── q2
   ├── start
   └── stop
├── deploy
├── cfg
   └── default.yml
└── lib
   ├── jpos-2.1.9-SNAPSHOT.jar
   ├── jdom2-
   ├── je-18.3.12.jar
   ├── jline-3.23.0.jar
   ├── snakeyaml-2.0.jar
   ├── HdrHistogram-2.1.12.jar
   ├── bcpg-lts8on-2.73.2.jar
   ├── bcprov-lts8on-2.73.2.jar
   ├── bsh-2.0b6.jar
   ├── commons-cli-1.4.jar
   ├── eddsa-0.3.0.jar
   ├── javatuples-1.2.jar
   ├── jcl-over-slf4j-1.7.32.jar
   ├── jdbm-1.0.jar
   ├── org.osgi.core-6.0.0.jar
   ├── slf4j-api-1.7.32.jar
   ├── sshd-common-2.9.2.jar
   ├── sshd-core-2.9.2.jar
   ├── ...
   └── ...

A jPOS application basically has a MANIFEST.MF with a Main-Class definition pointing to org.jpos.q2.Q2

Main-Class: org.jpos.q2.Q2

So in order to start it, you can call:

java -jar tutorial-0.0.1-SNAPSHOT.jar   # that's what the script bin/q2 basically do.

Note that we haven't defined the CLASSPATH in the previous example, we haven't used the -cp parameter with the java command. We don't need to do that. In jPOS applications, the classpath is set during the build process and is then included in the primary jar's MANIFEST.MF.

As a reference, in the provided example, besides the Main-Class, the MANIFEST.MF would contain entries similar to the following (that are automatically created by the jPOS Gradle plugin).

Main-Class: org.jpos.q2.Q2
Class-Path: lib/jdom2- lib/javatuples-1.2.jar lib/jline-3.2
3.0.jar lib/bsh-2.0b6.jar lib/commons-cli-1.4.jar lib/bcpg-lts8on-2.7
3.2.jar lib/bcprov-lts8on-2.73.2.jar lib/sshd-core-2.9.2.jar lib/sshd
-common-2.9.2.jar lib/jcl-over-slf4j-1.7.32.jar lib/slf4j-api-1.7.32.
jar lib/HdrHistogram-2.1.12.jar lib/org.osgi.core-6.0.0.jar lib/snake
yaml-2.0.jar lib/jdbm-1.0.jar lib/je-18.3.12.jar lib/eddsa-0.3.0.jar
... your.jar ... additional.jar ... dependencies.jar ...



The bin directory contains a few very simple scripts:

  • q2 runs Q2 in the foreground.
  • start uses nohup to start q2 in the background, and redirects stdout and stderr to /dev/null.
  • stop stops Q2.


Runtime dependencies are located in the lib directory.


In this location, jPOS services are detailed, with each service having its dedicated XML file that we call QBean descriptors. Q2 monitors this directory and manages the life-cycle of any file added, altered, or deleted from it.

For each descriptor found, Q2 instantiates and configures it, subsequently invoking its init method, followed by the start method. Conversely, if a file is removed, Q2 will first call the stop method and then proceed with the destroy method.

It's worth noting that the XML files are processed in alphabetical order.

Filenames are arbitrary, but there's a a special name, 00_logger.xml that is processed in advance of any other file and is expected to define the default Q2 logger. In addition, we suggest you keep the file 99_sysmon.xml that provides useful debuging metrics.

We have a numbering convention for these services that, although optional, you may want to adopt.

00_logger.xmlQ2 logger
01_*.xmlDatabase related services
05_*.xmlKey Management, HSM drivers, KeyStores
50_*.xmlISO and HTTP Servers
60_*.xmlDirPolls, Housekeeping services
90_*.xmlBatch jobs (Quartz)

Q2 supports DynamicClassLoading capabilities. Besides observing the deploy directory, it also keeps track of the deploy/lib directory. Any jar files added to this location become accessible to subsequently loaded QBeans.


QBeans often require configuration. While it's common practice to store these configurations in the cfg directory, they can be situated elsewhere if desired, e.g.:

   <property name="packager" value="cfg/mypackager.xml" />
<property name="key-store" value="cfg/keystore.cfg" />

In addition to individual configuration files, jPOS introduces the concept of "Environments." These are primarily utilized to differentiate configurations for production and development settings. Configuration for these environments resides in the cfg directory and carries a .yml (or .properties) extension. The standard configuration is found in cfg/default.yml, but it's typical to see cfg/devel.yml and cfg/prod.yml for development and production settings, respectively.


The use of jPOS environments can be used to manage database credentials sourced from key management systems, HSMs, and the like.


jPOS uses an Audit Log that stands apart from conventional line-loggers such as log4j or java.util.logging. While your application can opt for any logging framework of your choice, to ensure you're not operating jPOS in the dark without the capability to investigate communication concerns or delve into forensic analysis, it's essential to keep at least a few hours of jPOS audit active in production. For this, jPOS offers the maintenance-free RotateLogListener.


While logs can be stored at any location, it's common practice to keep the main Q2 log at log/q2.log. Should you encounter any issues or have queries requiring assistance on the jPOS users mailing list or support channels like Slack/Discord, we'll likely request the q2.log file to help diagnose and resolve your problem.


These logs are meticulously structured as XML files, designed for ease of analysis. This XML format allows for a comprehensive and structured representation of data, making it much simpler to identify and troubleshoot issues. It's important to maintain these logs in their original XML format; converting them into simplified, non-XML one-liners can significantly hinder your ability to receive effective assistance. Keeping the logs in XML ensures that all necessary details are preserved, facilitating a quicker and more accurate response from those assisting you.


jPOS adopts Maven's industry-standard directory structure. This means you'll encounter familiar directories such as src/main, src/main/java, src/main/resources, and the src/test/* directories designated for tests. When developing your application, you have the flexibility to use either a single top-level module, as demonstrated in this tutorial, or a multi-module approach akin to what's used in jPOS-EE.

The only addition to a regular build system is the src/dist directory.


In src/dist we mimic the production configuration, with the deploy directory, as well as cfg, bin, and a log placeholder (usually empty with a .gitkeep).

When you call gradle installApp (or gradle dist), all files in the src/dist directory get copied to the destination build/install directory (or tarball).


In production environments, configurations are typically stored separately. Adherents of 'GitOps' often utilize a distinct GitHub repository or an orphan branch for smaller projects. The distnc command (where 'nc' means 'no config') is available for creating a distribution devoid of configuration. However, for testing, continuous integration, and accelerated development cycles, including the development/staging configuration within the build proves to be extremely convenient.