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-2.0.6.1.jar
├── 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
├── ...
└── ...
(Version numbers may vary)
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 does.
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.
This approach makes deployments more resilient because the classpath is explicitly defined within the MANIFEST.MF
file. As a result, outdated or unused dependencies present in the lib
directory do not interfere with classpath resolution. Only the dependencies specified in the MANIFEST.MF
are utilized at runtime, ensuring that the application runs with the correct and up-to-date libraries. This isolation prevents potential conflicts and reduces the risk of runtime errors caused by unintended or obsolete jars, thereby enhancing the stability and reliability of deployments.
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-2.0.6.1.jar 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 ...
Runtime
bin
The bin
directory contains a few very simple scripts:
q2
runs Q2 in the foreground.start
usesnohup
to startq2
in the background, and redirectsstdout
andstderr
to/dev/null
.stop
stops Q2.
lib
Runtime dependencies are located in the lib
directory.
deploy
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.
Prefix | Description |
---|---|
00_logger.xml | Q2 logger |
01_*.xml | Database related services |
05_*.xml | Key Management, HSM drivers, KeyStores |
10_*.xml | Channels |
20_*.xml | MUXes |
30_*.xml | TransactionManager(s) |
40_*.xml | LogonManagers |
50_*.xml | ISO and HTTP Servers |
60_*.xml | DirPolls, Housekeeping services |
90_*.xml | Batch jobs (Quartz) |
99_sysmon.xml | SystemMonitor |
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.
cfg
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.
log
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.
We are currently mplementing Structured Audit Logs with JSON support, so this XML requirement may be lifted soon.
Development
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.
src/dist
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.