[[qtest]]
== QTest - a sample QBean 

Here is sample code for a simple test QBean. We'll call it +QTest+:

[source,java]
----
package org.jpos.qtest;

import org.jpos.iso.ISOUtil;
import org.jpos.q2.Q2;
import org.jpos.q2.QBean;
import org.jpos.util.Log;

public class QTest implements QBean, Runnable {
    volatile int state;
    long tickInterval = 1000;                                <1>
    Log log;

    public QTest () {
        super();
        state = -1;
        log = Log.getLog(Q2.LOGGER_NAME, "qtest");           <2>
        log.info ("constructor");
    }
    public void init () {
        log.info("init");
        state = STARTING;
    }
    public void start() {
        log.info ("start");
        state = STARTED;
        new Thread(this).start();
    }
    public void stop () {
        log.info ("stop");
        state = STOPPING;
    }
    public void destroy () {
        log.info ("destroy");
        state = STOPPED;
    }
    public void setTickInterval (long tickInterval) {
        this.tickInterval = tickInterval;
    }
    public long getTickInterval () {
        return tickInterval;
    }
    public void run () {
        for (int tickCount=0; running (); tickCount++) {
            log.info ("tick " + tickCount);
            ISOUtil.sleep (tickInterval);
        }
    }
    public int getState () {
        return state;
    }
    public String getStateAsString () {
        return state >= 0 ? stateString[state] : "Unknown";
    }

    private boolean running() {
        return state == QBean.STARTING || state == QBean.STARTED;
    }
}
----
<1> tickInterval is a custom attribute of this QBean
<2> in this example, we use the general purpose Q2 logger


[float]
=== Building QTest

The easiest way to play with jPOS is to use the link:https://github.com/jpos/jPOS-template[jPOS Template] project. 

Open a terminal (or Command window if you're on Windows), move to a temporary
directory and type:

----
git clone git@github.com:jpos/jPOS-template.git qtest

---[ output should look like this ]---
Cloning into 'qtest'...
remote: Counting objects: 165, done.
remote: Compressing objects: 100% (70/70), done.
remote: Total 165 (delta 82), reused 162 (delta 81)
Receiving objects: 100% (165/165), 87.34 KiB | 101 KiB/s, done.
Resolving deltas: 100% (82/82), done.
----

Then +cd+ to your newly created +qtest+ directory and try:

----
mkdir -p src/main/java/org/jpos/qtest
----

Copy and paste the previous code in a file named +QTest.java+.

[TIP]
=====
For your convenience, you can download the sources for +QTest+
and +QTestMBean+ classes from link:http://us.jpos.org/examples/qtest-1.0.0.jar[jPOS examples].
=====

Now create an XML file, (let's call it +90_qtest.xml+) like this in the +src/dist/deploy+ directory:

[source,xml]
----
<qbean name='qtest' class='org.jpos.qtest.QTest' />
----

Now run +gradle installApp+ or its handy abbreviation +gradle iA+
(see <<building>> for additional information about how to run Gradle or
 its wrapper +gradlew+ or +gradlew.bat+).

[NOTE]
=====
If you have +Gradle+ installed, you should be able to run the
previous command. Otherwise, there's a handy +gradlew+ 
(and +gradelw.bat+ if you're on Windows).
=====

This is not going to work, but it's worth to run it and see the error
so you can understand how Q2 loads its QBeans, which are actually 
link:http://docs.oracle.com/javase/tutorial/jmx/mbeans/[JMX MBeans].

The +gradle installApp+ command should have created a jPOS application
in the +build/install/qtest+ directory, so you can navigate there 
(+cd buildl/install/qtest+) and call +bin/q2+ (or +bin\q2.bat+ if 
you are on Windows).

[TIP]
====
If you don't want to navigate to the +build/install/qtest+ directory,
you can call +gradle run+ in the top level directory of the project
or module. This is of course a bad idea for production as you would
be loading Gradle in memory for no reason.
====

After running it, you should see output like this:

[source,xml]
----
<log realm="Q2.system" at="Sun Oct 20 16:16:47 UYST 2013.61">
 <warn>
  Tidying build/install/qtest/deploy/90_qtest.xml out of the            <1>
  way, by adding .BAD
 </warn>
</log>

<log realm="Q2.system" at="Sun Oct 20 16:16:47 UYST 2013.62" lifespan="5ms">
 <info>
  deploy: /private/tmp/test/qtest/build/install/qtest/deploy/90_qtest.xml
  <exception name="MBean class org.jpos.test.QTest does not implement   <2>
DynamicMBean, and neither follows the Standard MBean conventions
(javax.management.NotCompliantMBeanException: Class org.jpos.test.QTest 
is not a JMX compliant Standard MBean) ...
  ...
  ...
 </info>
</log>
----
<1> Q2 detects that there's a problem with this QBean. In order to
    prevent the problem from happening again, it renames it to an
    extension other than +.xml+, and as an eye-catcher, it calls it
    +.BAD+.
<2> The reason for the error is shown below: +QTest+ is a not compliant
    MBean and can't be loaded+.

Q2 uses a JMX MbeanServer to create instances of QBeans, and JMX expects
to pick some information about these classes using and interface named
after the class name and ending with +MBean+.

So if we are loading a class called +org.jpos.test.QTest+, the JMX MBeanServer
will attempt to load an interface called +org.jpos.test.QTestMBean+ first,
if it's not there, it won't load your QBean.

Now let's create that simple MBean file and place it in 
+src/main/java/org/jpos/test/QTestMBean.java+. 
It looks like this:

[source,java]
----
package org.jpos.qtest;

import org.jpos.q2.QBean;

public interface QTestMBean extends QBean {
    public void setTickInterval(long tickInterval) ;
    public long getTickInterval() ;
}
----

In addition, we need to change our +QTest+ so that it +implements QTestMBean+.
Because +QTestMBean+ extends +QBean+, we can change:

[source,java]
----
public class QTest implements QBean, Runnable {
 ...
 ...
}
----
so that it reads

[source,java]
----
public class QTest implements QTestMBean, Runnable {
 ...
 ...
}
----

Now if you run +build/install/qtest/bin/q2+ you'll see messages like:

[source,xml]
----
<log realm="qtest" at="Sun Oct 20 16:51:27 UYST 2013.28">
  <info>
    init
  </info>
</log>
<log realm="qtest" at="Sun Oct 20 16:51:27 UYST 2013.35">
  <info>
    start
  </info>
</log>
<log realm="qtest" at="Sun Oct 20 16:51:27 UYST 2013.37" lifespan="1ms">
  <info>
    tick 0
  </info>
</log>
...
...
<log realm="qtest" at="Sun Oct 20 16:51:28 UYST 2013.38">
  <info>
    tick 1
  </info>
</log>
...
...
<log realm="qtest" at="Sun Oct 20 16:51:29 UYST 2013.40">
  <info>
    tick 2
  </info>
</log>
----

Approximately every second we see a 'tick' message, issues by our little
+run()+ method:

[source,java]
----
    public void run () {
        for (int tickCount=0; running (); tickCount++) {
            log.info ("tick " + tickCount);
            ISOUtil.sleep (tickInterval);
        }
    }
----

While Q2 is running and 'ticking', you can launch +jconsole+, connect
to the running process and navigate to the +QTest+ QBean attributes to
see the +tickInterval+. You are free to change it to another value and
that will change the behavior of the running QTest QBean.

The screen will look something like this:

image:images/qtest_interval_jconsole.png[width="400px",alt="QTest/jConsole"]

[NOTE]
====
If you are running Q2 using the +gradle run+ tasks, you'll find out
you won't get to see the Q2 MBean under the MBeans tabs, you'll see
just the system MBeans.

The reason for this is that +com.sun.management.jmxremote+ option
is not set by default. If you're running the +bin/q2+ script, there's
a +-Dcom.sun.management.jmxremote+ in the JVM invocation and that's the
reason the Q2 MBeans can be managed.
====

[float]
=== PUSH configuration - Setting QBean attributes 

In the same way you can use +jconsole+ to tweak the QBean attributes
defined in the MBean, you can use the XML 'attr' element in the
QBean descriptor. Q2 will use the MBeanServer to send them via JMX.

So you can change the +90_qtest.xml+ file (in the +src/dist/deploy+) 
directory to look like this:

[source,xml]
----
<qbean name='qtest' class='org.jpos.qtest.QTest'>
  <attr name="tickInterval" type="java.lang.Long">5000</attr>
</qbean>
----

[TIP]
====
If no 'type' attribute, the default is 'java.lang.String'.
+java.lang.Long+ can be abbreviated as just +long+, same goes
for +int+ (+java.lang.Integer) and +boolean+ (+java.lang.Boolean+)
====

[float]
=== PULL configuration - implementing Configurable

Pushing configuration using attributes provides a lot of runtime
flexibility, but requires a lot of boilerplate code with the MBean
interfaces. Sometimes it's easier to just implement the very simple
link:http://jpos.org/doc/javadoc/org/jpos/core/Configurable.html[Configurable]
interface and adding a few child +property+ elements in the QBean
descriptor.

Let's change our QTest class to read like this:

[source,java]
----
package org.jpos.test;

import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.iso.ISOUtil;
import org.jpos.q2.Q2;
import org.jpos.q2.QBean;
import org.jpos.util.Log;

public class QTest implements QTestMBean, Runnable, Configurable {          <1>
    volatile int state;
    long tickInterval = 1000;
    Log log;
    boolean debug;                                                          <2>

    public QTest () {
        super();
        state = -1;
        log = Log.getLog(Q2.LOGGER_NAME, "qtest");
        log ("constructor");
    }
    public void init () {
        log ("init");
        state = STARTING;
    }
    public void start() {
        log ("start");
        state = STARTED;
        new Thread(this).start();
    }
    public void stop () {
        log ("stop");
        state = STOPPING;
    }
    public void destroy () {
        log ("destroy");
        state = STOPPED;
    }
    public void setTickInterval (long tickInterval) {
        this.tickInterval = tickInterval;
    }
    public long getTickInterval () {
        return tickInterval;
    }
    public void run () {
        for (int tickCount=0; running (); tickCount++) {
            log.info ("tick " + tickCount);
            ISOUtil.sleep (tickInterval);
        }
    }
    public int getState () {
        return state;
    }
    public String getStateAsString () {
        return state >= 0 ? stateString[state] : "Unknown";
    }
    public void setConfiguration (Configuration cfg) {                      <3>
        debug = cfg.getBoolean("debug", true);
    }

    private boolean running() {
        return state == QBean.STARTING || state == QBean.STARTED;
    }
    private void log (String message) {
        if (debug)                                                          <4>
            log (message);
    }
}
----
<1> Implement +Configurable+
<2> add a new 'debug' boolean
<3> Actual implementation of the +Configurable+ interface, picks the +debug+
    property from the XML configuration, defaulting to +true+
<4> Honor the debug property.
   
Now the +src/dist/deploy/90_qtest.xml+ file would look like this:

[source,xml]
----
<qbean name='qtest' class='org.jpos.test.QTest'>
  <property name="debug" value="false" />
</qbean>
----

If you want to set your properties in a separate file, you could 
+<property file="xxx" /> instead of +<property name="xx" value="yy" />+, 
i.e:

[source,xml]
----
<qbean name='qtest' class='org.jpos.test.QTest'>
  <property file="cfg/myconfig.cfg" />
</qbean>
----

and then add a file +src/dist/cfg/myconfig.cfg+, e.g.:

----
debug=false
----


[TIP]
=====
If the file name ends in `.yml`, Q2 handles it as a YAML file.
=====

The files in the +src/dist+ directory get copied to +build/install+ when
we call +gradle installApp+ or to the +build/distributions+ when we call
+gradle dist+ and are subject to property expansion.

So if instead of writing +debug=false+, you put +debug=@debug@+ (same goes if you use +<property name="debug" value="@debug@" />+), and you add a compile-time
property called +debug+ to your compile 'target', Gradle will propertly
replace it when copying it to the destination directory.

In order to test this lets change the file in +src/dist/deploy/90_qtest.xml+ 
to read like this:

[source,xml]
----
<qbean name='qtest' class='org.jpos.test.QTest'>
  <property name="debug" value="@debug@" />
</qbean>
----

And add a top level file called +devel.properties+ with a line like this:

----
debug=yes
----

[TIP]
====
Yes, Q2 understand 'yes' and 'no' in addition to 'true' and 'false'
====

When you call +gradle installApp+, the destination file in
+build/install/qtest/deploy/90_qtest.xml+ will have a +yes+ instead of
the +@debug@+ token.

+devel+ is the default Gradle target defined by jPOS and that's the
reason it reads the +devel.properties+ file. But you can override the
target using the +-Ptarget=xxx+ parameter, so you can for example
create a file called +prod.properties+ where +debug=no+ and then
call +gradle -Ptarget=prod clean installApp+.

[NOTE]
====
Please note we've added +clean+ as part of the build, reason is because
the source file +src/dist/deploy/90_qtest.xml+ didn't change, and the
destination file +build/install/qtest/deploy/90_qtest.xml+ was created
in the previous step (with the default +devel+ target), Gradle assumes
the file is up-to-date and do not attempt to re-generate it.
====

[TIP]
====
If you prefer to have more control over the XML inside your QBeans,
like the one we use in the ChannelAdaptor, QMUX or the TransactionManager
where we have child elements with their own hierarchy (like 'filters',
'participants', 'queues'), you can implement +org.jpos.core.XmlConfigurable+
instead of +Configuration+ so that instead of a flat +Configuration+
object, you receive an +org.jdom.Element+ that you can use to interpret
your own configuration.
====

[float]
=== Honoring the 'logger' and 'realm' attributes

Q2 uses reflection to find out if a QBean has a method with the
following signature: +void setLogger (String loggerName)+, and
and optional +void setRealm (String realm)+. 

We can take advantage of that feature by adding 
the following code to our QTest file:

[source,java]
----
    public void setLogger (String loggerName) {
        log = Log.getLog (loggerName, getClass().getName());
        setModified (true);
    }
    public void setRealm (String realm) {
        if (log != null)
            log.setRealm (realm);
    }
----

[TIP]
====
If you are starting to get worried about the large number
of options you have when implementing a QBean, don't worry,
there's a handy support class called +QBeanSupport+ that you
can extend in order to take advantage of all these features
without having to write a lot of boilerplate code. We'll show
you how to use it shortly, but if you want to understand how
Q2 works, we suggest you follow this lengthly step-by-step 
explanation.
====

[float]
=== Getting a reference to the Q2 server

If your QBean needs a reference to the Q2 server, it can implement the
+setServer(Q2 server)+ method. Q2 will push a reference to itself at
configuration file.

[float]
=== Getting a reference to the XML element representing the QBean descriptor

If your QBean has a method with the signature +void setPersist(Element e)+,
Q2 will push the Element representing the QBean descriptor. This feature
allows a QBean to implement the +QPersist+ interface, that looks like this:

[source,java]
----
public interface QPersist { 
    public Element getPersist ();
    public boolean isModified ();
}       
----

If your +QBean+ implements +QPersist+ and its +isModified()+ 
returns +true+, then Q2 will call its +getPersist()+ to get a new
QBean descriptor and will store it in the +deploy+ directory.

[TIP]
====
This feature is rarely used in jPOS applications, but it's there just
in case you want to experiment with it. In our previous +jconsole+ example,
a change to the +tickInterval+ done via JMX could be stored in the
+90_qtest.xml+ file automatically, so it can be honored on the next
restart.
====

[NOTE]
====
The name 'persist' here is a really bad name, something like
+getXmlDescriptor()+ could have been better.
====

