Skip to main content

QBean Attributes

XML Descriptor Attributes

name

The QBean XML descriptors recognize several XML attributes. Among these, we've previously discussed the name attribute, which by default corresponds to the name of the top-level root element. To revisit and consolidate our understanding, let's look at an example:

A QBean named myservice can be defined as:

<myservice ...>
...
...
</myservice>

or

<anyname name='myservice' ...>
...
...
</anyname>
note

In cases where a clear and distinct name for the top-level element is not available, the convention is to use qbean. This approach helps to indicate that the XML fragment in question serves as a QBean descriptor.

class

<qbean name='myservice' class='com.your.company.YourService'>
...
...
</qbean>

Unless you're using a common name defined in QFactory.properties, a class attribute is required.

Here is a sample QFactory.properties file:

logger=org.jpos.q2.qbean.LoggerAdaptor
shutdown=org.jpos.q2.qbean.Shutdown
script=org.jpos.q2.qbean.BSH
sysmon=org.jpos.q2.qbean.SystemMonitor
txnmgr=org.jpos.transaction.TransactionManager
transaction-manager=org.jpos.transaction.TransactionManager
qmux=org.jpos.q2.iso.QMUX
qserver=org.jpos.q2.iso.QServer
channel-adaptor=org.jpos.q2.iso.ChannelAdaptor
qexec=org.jpos.q2.qbean.QExec
config=org.jpos.q2.qbean.QConfig
xml-config=org.jpos.q2.qbean.QXmlConfig
ks=org.jpos.q2.security.KeyStoreAdaptor
templates=org.jpos.q2.qbean.TemplateDeployer
prometheus=org.jpos.metrics.PrometheusService
...
...

So if you're defining a TransactionManager, instead of using:

<qbean name='TXNMGR' class='org.jpos.transaction.TransactionManager' ...>
...
...
</qbean>

You can use:

<txnmgr name='TXNMGR' ...>
...
...
</txnmgr>

or:

<transaction-manager name='TXNMGR' ...>
...
...
</transaction-manager>

as both txnmgr as well as transaction-manager are mapped to the same class, in this case org.jpos.transaction.TransactionManager.

logger and realm

In many well-known logging frameworks, loggers are typically linked to specific classes. However, in jPOS, a single Q2 system can manage thousands of connections with various remote endpoints. To efficiently organize these components, jPOS employs the concept of realm. This is a free-text identifier used to group together elements associated with a particular network, allowing for more streamlined and effective management of multiple connections.

Imagine the following services:

<txnmgr name='TXNMGR.ACQ' logger='Q2' realm='acquiring'>
...
...
</txnmgr>

<txnmgr name='TXNMGR.ISS' logger='Q2' realm='issuing'>
...
...
</txnmgr>

Q2 adopted what is known today as IoC (Inversion of Control) or "Push Configuration," prior to these concepts becoming widely recognized in the industry. This implementation occurred before Java 5 introduced annotations. In Q2, a simple but effective convention is used: if a QBean has a public method with the following signature, reflection is employed to call it:

public void setLogger (Logger logger, String realm);
note

The `setLogger`` method is part of the org.jpos.util.LogSource interface. However, it's not mandatory for a QBean to implement this interface to have a logger pushed to it, as Q2 leverages reflection for this purpose.

enabled

To disable a QBean, you can simply remove its XML descriptor from the deploy directory or change its extension from .xml to something else. A typical practice is to rename these files to .off, and then back to .xml when needed again. This method works well during testing and QA phases. However, as the number of services increases and you work across multiple environments (such as development, QA, and production), a more efficient approach is to use the enabled attribute in the XML descriptor. For a service to start, this attribute, if included, must be set to true. Any value other than true is interpreted as the service being disabled.

<txnmgr name='TXNMGR.ACQ' logger='Q2' realm='acquiring' enabled='true'>
...
...
</txnmgr>

<txnmgr name='TXNMGR.ISS' logger='Q2' realm='issuing' enabled='false'>
...
...
</txnmgr>
note

As you delve further into these tutorials, you'll learn about sourcing attributes from a range of sources, including Java Properties, operating system environment variables, and database connections. Specifically for managing confidential information, we'll cover methods for obtaining secrets from hardware security modules. In the 'Configuration' section, everything comes together. You'll see how to effectively use attributes like enabled='${acquiring.enabled:false}' defined in top-level YAML files, integrating all these components seamlessly.

instance

TL;DR

While this is mainly internal information and might not be frequently used, it's included here for the sake of completeness.

Let's break down Q2's instanceId and how it integrates with Q2's deployment process for clarity:

  1. Understanding instanceId: Each time Q2 starts, it generates a unique, random UUID known as the instanceId.

  2. Deployment Process in Q2:

    • Reading Static Files: Q2 initiates services by reading static files located in the deploy directory.
    • Dynamic Service Deployment: Additionally, Q2 has the capability to dynamically deploy services. This can be done in two ways:
      • Automatically by Q2: For instance, when you execute q2 --ssh, Q2 internally deploys a service using a template XML descriptor. An example of such a descriptor looks like this:

        <sshd name="sshd" instance="00ddcec7-076a-443e-836e-cfdb81222d23">
        <property name="port" value="2222" />
        <property name="auth-username" value="admin" />
        <property name="authorized-keys-file" value="cfg/authorized_keys" />
        <property name="hostkeys-file" value="cfg/hostkeys.ser" />
        </sshd>

        This XML is then saved as a file, for example, 05_sshd-00ddcec7-076a-443e-836e-cfdb81222d23.xml.

      • Programmatically by Users: Users can deploy services using the Q2.deployElement method.

  3. Instance-Specific Services:

    • The services deployed, such as the SSHD service in the example, are meant to run only in the specific Q2 instance that deployed them. They are marked with the deleteOnExit flag, which ideally deletes the file upon JVM exit.
    • However, deleteOnExit has limitations — it may not work if the JVM crashes or is terminated abruptly (e.g., via SIGKILL).
  4. Safety Mechanism with instanceId:

    • To prevent these transient services from unintentionally running in subsequent Q2 instances, each service is linked to the instanceId of the Q2 instance that deployed it.
    • Upon a new start, if the instance attribute in the top level XML document doesn’t match the current Q2’s instanceId, the service is not executed and is deleted, mimicking the intended behavior of the deleteOnExit flag.

This approach ensures that transient services, dynamically deployed during a specific Q2 instance, are tightly bound to that instance and don't inadvertently persist beyond their intended lifecycle.

JMX attributes

TL;DR

This section describes the process of assigning JMX attributes to your QBeans using XML attr elements. Although this approach was quite pioneering before Java 5 introduced annotations, there are now more modern methods available (such as the @Config annotation), which will be covered in subsequent sections. This content is included for completeness, particularly as some components, like QServer, still employ this method.

Q2 recognizes the `attr`` element within the top-level qbean element. For example:

<qserver name="iso-server" logger='Q2' realm-'server-simulator'>
<attr name="port" type="java.lang.Integer">8000</attr>
...
...
</server>

In this snippet, after the QBean is instantiated but before its life-cycle process begins, a method named public void setPort(int port); is invoked via JMX.

It's important to emphasize via JMX, highlighted in bold, to underline the distinction between directly invoking a set operation on an object's attribute and doing so indirectly through JMX using its ObjectName. This indirect method via JMX offers highly flexible scenarios, enabling interactions with target objects that might be remote, under supervision, etc., which adds a layer of versatility not present in direct attribute manipulation.

This configuration can be dynamically sourced from various origins, such as Java properties, YAML files, or environment variables. Consider this example:

<myservice name="iso-server" logger='Q2' realm-'server-simulator'>
<attr name="password" type="java.lang.String">${system.password}</attr>
...
...
</myservicer>

Here's an intriguing glimpse: the `system.password`` can be defined in a YAML file as follows or sourced from an environment variable.

system:
password: hsm::Jc9u9/baBMgOougS1c2Labw/Nr+zxJrlYkROGRaNT9U=

In this scenario, the configuration subsystem employs a ServiceLoader specifically designed to handle entries prefixed with hsm::. It then securely forwards the provided data—in this case, an encrypted string—to a Hardware Security Module (HSM) for decryption. This mechanism ensures that sensitive information like passwords is managed securely and efficiently, harnessing the power of HSM for robust encryption and decryption processes.