Qorus Integration Engine®  4.0.3.p1_git
Developing in Java

Introduction to Java Development in Qorus

Qorus supports native Java development where code attributes of objects can be defined with code in Java in addition to Qore.

Java integration with Qore is implemented using the jni module, which provides tight integration with both languages.

The main Qorus Java APIs are:

Java code is defined in class objects, and classpath entries for each object can be specified using the classpath class object tag; see Class Definition File and Java Classpath Handling in Qorus for more information and examples.

To use Java with Qore, a compatible JDK must be installed on the Qorus server machine; see

See also
Java Requirements

Qorus Java Source File Names

Source files should have the same naming convention as other Qorus objects, however the following additions to the standard Qorus naming convention can help properly-configured editors to perform source highlighting for Qorus Java sources properly as in the following table.

Object Pattern Example
Qorus classes name-vversion.qclass.java MyJavaClass-v1.0.qclass.java
Qorus services name-vversion.qsd.java MyJavaService-v1.0.qsd.java
Qorus jobs name-vversion.qjob.java MyJavaJob-v1.0.qjob.java

Java Classpath Handling in Qorus

Generally, the Java classpath is set with the object tag classpath when defining Java code in Qorus. Furthermore, the recommended storage location for jar or class files is in $OMQ_DIR/user/jar.

The classpath tag accepts environment variables, and each element in the classpath should be separated by a colon (":"); see the following examples for more information.

In class definitions, the Java classpath is set as in the following example.

Example (MyJavaClassExample-v1.0.qclass.java):
// name: MyJavaClassExample
// version: 1.0
// desc: Java example class
// author: Qore Technologies, s.r.o.
// lang: java
// TAG: classpath: $OMQ_DIR/user/jar/my-jar-1.jar:$OMQ_DIR/user/jar/my-jar-2.jar
package com.qoretechnologies.qorus.example;
// inherit UserApi to make accessing static members and fields easier
public class MyJavaClassExample extends UserApi {
public MyJavaTest() {
log(LL_INFO, "my example class");
}
}
// END
Note
the classpath tag can also be set in Java jobs and services as well

Using Java in Qorus Workflows

Qorus workflows can be defined in Java by implementing a class-based step and subclassing one of the following step classes for the step:

Compiling Byte Code for Qorus Workflows

In order to compile byte code for Qorus workflows, the following jar files need to be in the classpath at a minimum:

  • $OMQ_DIR/jar/qore-jni.jar: provides the low-level API connecting Qore and Java
  • $OMQ_DIR/jar/qorus-common.jar: the base Qorus API common to all interfaces
  • $OMQ_DIR/jar/qorus-workflow.jar: the Qorus workflow API

ex:

javac -cp $OMQ_DIR/jar/qore-jni.jar:$OMQ_DIR/jar/qorus-common.jar:$OMQ_DIR/jar/qorus-workflow.jar MyClass.java 

Using Java in Qorus Services

Qorus services can be defined in Java by implementing a class-based service and subclassing the QorusService class and providing metadata for service methods using annotations as in the following example (note the use of the classpath tag to provide the classpath for services).

Example (java-example-service-v1.0.qsd.java):
// service: java-example-service
// serviceversion: 1.0
// patch: p1
// lang: java
// class-name: MyJavaService
// remote: true
// servicedesc: example service
// define-group: EXAMPLES: example interface objects
// groups: EXAMPLES
// TAG: example-code: true
// TAG: classpath: $OMQ_DIR/user/jar/my-jar-1.jar:$OMQ_DIR/user/jar/my-jar-2.jar
// ENDSERVICE
package com.qoretechnologies.qorus.example;
import java.time.ZonedDateTime;
class MyJavaService extends QorusService {
public static final ZonedDateTime systemStarted;
static {
try {
systemStarted = (ZonedDateTime)callRestApi("GET", "system/starttime");
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@QorusMethod(
desc = "initializes the service"
)
public void init() {
}
@QorusMethod(
desc = "an example static service method"
)
public static void other() {
}
}

Compiling Byte Code for Qorus Services

In order to compile byte code for Qorus services, the following jar files need to be in the classpath at a minimum:

  • $OMQ_DIR/jar/qore-jni.jar: provides the low-level API connecting Qore and Java
  • $OMQ_DIR/jar/qorus-common.jar: the base Qorus API common to all interfaces
  • $OMQ_DIR/jar/qorus-service.jar: the Qorus service API

ex:

javac -cp $OMQ_DIR/jar/qore-jni.jar:$OMQ_DIR/jar/qorus-common.jar:$OMQ_DIR/jar/qorus-service.jar MyClass.java 

Using Java in Qorus Jobs

Qorus jobs can be defined in Java by implementing a class-based job and subclassing the QorusJob class as in the following example (note the use of the classpath tag to provide the classpath for jobs).

Example (java-example-job-v1.0.qjob.java):
// name: java-example-job
// version: 1.0
// desc: test job, run every 10 minutes every hour, every weekday
// author: Qore Technologies, s.r.o.
// class-name: MyJavaExampleJob
// active: true
// run-skipped: true
// schedule: */10 * * * mon-fri
// define-group: EXAMPLES: example interface objects
// groups: EXAMPLES
// lang: java
// TAG: classpath: $OMQ_DIR/user/jar/my-jar-1.jar:$OMQ_DIR/user/jar/my-jar-2.jar
package com.qoretechnologies.qorus.example;
class MyJavaExampleJob extends QorusJob {
// the run method implements the job's logic
public void run() throws Throwable {
log(LL_INFO, "test job info: %y", getInfo());
}
}
// END

Compiling Byte Code for Qorus Jobs

In order to compile byte code for Qorus jobs, the following jar files need to be in the classpath at a minimum:

  • $OMQ_DIR/jar/qore-jni.jar: provides the low-level API connecting Qore and Java
  • $OMQ_DIR/jar/qorus-common.jar: the base Qorus API common to all interfaces
  • $OMQ_DIR/jar/qorus-job.jar: the Qorus job API

ex:

javac -cp $OMQ_DIR/jar/qore-jni.jar:$OMQ_DIR/jar/qorus-common.jar:$OMQ_DIR/jar/qorus-job.jar MyClass.java 

Restrictions on Java APIs

Java is not subject to sandboxing controls like Qore code, so it's easier to do dangerous things with Java.

All normal Java programming best practices should be followed when programming in Java; make sure all resources are freed in finally blocks and so forth; Qorus can only manage Qore resources, the Java JVM manages all Java resources normally.

Do not do any of the following:

  • Do not try to call any Qorus functionality from a Java thread not created by Qorus
  • Generally you should not create Java threads; any backgrgound functionality should be handled in a Qorus service
  • Do not make any calls that affect the current running process (ex: System.exit(), Runtime.exit() or Runtime.halt() and similar)

Java Classes Wrapping a Qore Object

Java classes that wrap Qore classes manage a weak reference to the Qore object using the QoreObject class, normally by wrapping the object with the QoreObjectWrapper class. Strong references to the Qore objects are always managed in Qorus, which means that when creating or acquiring a Qore object in Qorus with Java, the object will normally go out of scope after control returns to Qorus, meaning that the Qore destructor is then run, so that Qore's deterministic garbage collector can also be used in Java code.

For example, locks are released when DynamicDataHelper objects are collected after created from Java step code. The same applies to TempDataHelper or SensitiveDataHelper objects.

Writing Tests in Java for Qorus Interfaces

The following classes can help to write tests written in Java:

  • QorusWorkflowTest
  • QorusServiceTest
  • QorusJobTest

The Qorus test APIs are found in the qorus-test.jar file; ex:

    # compile:
    javac -cp ${OMQ_DIR}/jar/qorus-client.jar:${OMQ_DIR}/jar/qorus-test.jar:${OMQ_DIR}/jar/qore-jni.jar MyTest.java
    # run:
    java -Djava.library.path=$OMQ_DIR/lib/libqore.so -cp ${OMQ_DIR}/jar/qorus-client.jar:${OMQ_DIR}/jar/qorus-test.jar:${OMQ_DIR}/jar/qore-jni.jar:. MyTest
See also
com.qoretechnologies.qorus.test
Example Workflow Test
import com.qoretechnologies.qorus.client.*;
import com.qoretechnologies.qorus.test.*;
import java.util.HashMap;
class TestWorkflow {
public static void main(String[] args) throws Throwable {
QorusClientCore.init2();
QorusWorkflowTest test = new QorusWorkflowTest("SIMPLETEST", "1.0");
test.addTestCase("wf test", () -> testWorkflow(test));
test.main();
}
@SuppressWarnings("unchecked")
private static void testWorkflow(QorusWorkflowTest test) throws Throwable {
HashMap<String, Object> static_data = new HashMap<String, Object>();
HashMap<String, Object> order_hash = new HashMap<String, Object>();
order_hash.put("staticdata", static_data);
HashMap<String, Object> result = test.execSynchronous(order_hash);
test.assertEq(OMQ.StatComplete, result.get("status"));
QorusSystemRestHelper qrest = new QorusSystemRestHelper();
QorusClientAPI omqclient = new QorusClientAPI();
HashMap<String, Object> rv = (HashMap<String, Object>)qrest.putRest("workflows/SIMPLETEST/setAutostart?autostart=1");
try {
test.exec(new CheckRunningWorkflow("SIMPLETEST"));
// create a workflow order
int wfiid = omqclient.createWorkflowInstanceName("SIMPLETEST", "1.0", new HashMap<String, Object>());
String name = test.getQorusInstanceName();
test.assertTrue(name != null);
// workaround for bug #2884:
qrest.putRest("workflows/SIMPLETEST/reset");
test.exec(new WaitForWfiid(wfiid));
test.assertEq(OMQ.StatComplete, qrest.getRest("orders/" + wfiid + "/workflowstatus"));
} finally {
qrest.putRest("workflows/SIMPLETEST/setAutostart?autostart=0");
}
}
}
Example Service Test
import com.qoretechnologies.qorus.client.*;
import com.qoretechnologies.qorus.test.*;
import java.util.HashMap;
class TestService {
public static void main(String[] args) throws Throwable {
QorusClientCore.init2();
QorusServiceTest test = new QorusServiceTest("http-test");
test.addTestCase("svc test", () -> testService(test));
test.main();
}
private static void testService(QorusServiceTest test) throws Throwable {
CallService call = new CallService("http-test", "echo", 1);
test.exec(call);
test.assertEq(1, ((Object[])call.getResult())[0]);
}
}
Example Job Test
import com.qoretechnologies.qorus.client.*;
import com.qoretechnologies.qorus.test.*;
import java.util.HashMap;
class TestJob {
public static void main(String[] args) throws Throwable {
QorusClientCore.init2();
QorusJobTest test = new QorusJobTest("test");
test.addTestCase("job test", () -> testJob(test));
test.main();
}
private static void testJob(QorusJobTest test) throws Throwable {
RunJob action = new RunJob();
test.exec(action);
action = new RunJobResult(OMQ.StatComplete);
test.exec(action);
}
}
Note
The test classes anove are based on the Test class which provides a wrapper for the QUnit module in Qore.

Qorus Java Client API

The Java Qorus client API can be found in the com.qoretechnologies.qorus.client package which is delivered in the qorus-client.jar file; ex:

    # compile:
    javac -cp ${OMQ_DIR}/jar/qorus-client.jar:${OMQ_DIR}/jar/qore-jni.jar MyQorusClient.java
    # run:
    java -Djava.library.path=$OMQ_DIR/lib/libqore.so -cp ${OMQ_DIR}/jar/qorus-client.jar:${OMQ_DIR}/jar/qore-jni.jar:. MyQorusClient

See the following section for runtime dependency information for the Qore library when using the Java Qorus client.

Java APIs Based on Qore Functionality

Java libraries for Qorus that depend on the native Qore library and the jni module to provide a wrapper for underlying Qore functionality such as the JAva client or test APIs must be run with either the Java runtime option java.library.path or the QORE_LIBRARY environment variable set to the location of the native Qore library.

See also
Using the jni Module From Java for more information