Back to the Developer's Guide Table of Contents
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, including dynamic imports into Java using bytecode generation of wrapper classes for deep language integration based on introspection / reflection of the imported language (Qore or Python) and runtime data conversions.
All Qore APIs are available in Java using dynamic imports using the special "qore" package.
The main Qorus APIs are:
- UserApi: available as
qore.OMQ.UserApi.UserApi
- WorkflowApi: available as
qore.OMQ.UserApi.Workflow.WorkflowApi
- ServiceApi: available as
qore.OMQ.UserApi.Service.ServiceApi
- JobApi: available as
qore.OMQ.UserApi.Job.JobApi
- See also
- Importing Qore and Python APIs Dynamically in Java
Java code is defined in class objects, and classpath entries for each object can be specified using the classpath tag in a class object; see 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 Java Requirements
- Note
- The following hardcoded Java APIs available in Qorus JAR files are now deprecated and have not been updated since Qorus 5.0; they are a subset of the dynamic APIs described above:
- UserApi
- WorkflowApi
- ServiceApi
- JobApi
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 with the tag classpath as in the following example:
- Example Classpath Tag Value
classpath: $OMQ_DIR/user/jar/my-jar-1.jar:$OMQ_DIR/user/jar/my-jar-2.jar
Compiling Java to Bytecode for Qorus
Either oload or the QoreJavaCompiler must be used to compile Java source using dymamic imports to bytecode.
Both oload and QoreJavaCompiler require the Qore jni JAR file to be available as well:
$OMQ_DIR/jar/qore-jni.jar: provides the low-level API connecting Qore and Java
The standard Java compiler cannot be used if your Qorus code uses dynamic API imports.
Using Java in Qorus Workflows
Qorus workflows can be defined in Java by implementing a step class by subclassing one of the following step classes for the step:
- Note
- The old Qorus-specific JAR files with hardcoded Qorus APIs are deprecated and are no longer receiving updates since Qorus 5.0. The following jar files were used when developing with the deprecated APIs for workflows:
$OMQ_DIR/jar/qore-jni.jar: provides the low-level API connecting Qore and Java
$OMQ_DIR/jar/qorus-common.jar: (deprecated) the base Qorus API common to all interfaces
$OMQ_DIR/jar/qorus-workflow.jar: (deprecated) the Qorus workflow API
Using Java in Qorus Services
Qorus services can be defined in Java by subclassing the QorusService class, available as qore.OMQ.UserApi.Service.QorusService in Java
- Note
- The old Qorus-specific JAR files with hardcoded Qorus APIs are deprecated and are no longer receiving updates since Qorus 5.0. The following jar files were used when developing with the deprecated APIs for services:
$OMQ_DIR/jar/qore-jni.jar: provides the low-level API connecting Qore and Java
$OMQ_DIR/jar/qorus-common.jar: (deprecated) the base Qorus API common to all interfaces
$OMQ_DIR/jar/qorus-service.jar: (deprecated) the Qorus service API
Deprecated Service Method Annotations
Service method annotations, meant to support the development of Qorus service code in JAR or class files without YAML metadata are deprecated, as the hardcoded JAR files are no longer necessary with the dynamic Java API support introduced in Qorus 5.1, and YAML-metadata-based services are the primary recommended and supported way to implement Qorus services.
When developing service classes or base classes to be delivered in binary format (jar or class files), Qorus service methods must be declared with the QoreMethod annotation as in the following example:
- Example (MyJavaService.java):
package com.qoretechnologies.qorus.example;
import qore.OMQ.UserApi.UserApi;
import qore.OMQ.UserApi.Service.*;
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() throws Throwable {
}
@QorusMethod(
desc = "an example static service method"
)
public static void other() throws Throwable {
}
}
Using Java in Qorus Jobs
Qorus jobs can be defined in Java by implementing a job class and subclassing the QorusJob class (available as qore.OMQ.UserApi.Job.QorusJob in Java) as in the following example (note the use of the classpath tag to provide the classpath for jobs).
- Example Classpath Tag Value
classpath: $OMQ_DIR/user/jar/my-jar-1.jar:$OMQ_DIR/user/jar/my-jar-2.jar
- Example (java-example-job-v1.0.qjob.java):
package com.qoretechnologies.qorus.example;
import qore.OMQ.UserApi.Job.QorusJob;
class MyJavaExampleJob extends QorusJob throws Throwable {
public void run() throws Throwable {
logInfo(
"test job info: %y", getInfo());
}
}
static logInfo(softstring msg,...)
Writes the information passed to the workflow, service, job, or system log file depending on the call...
- Note
- The old Qorus-specific JAR files with hardcoded Qorus APIs are deprecated and are no longer receiving updates since Qorus 5.0. The following jar files were used when developing with the deprecated APIs for jobs:
$OMQ_DIR/jar/qore-jni.jar: provides the low-level API connecting Qore and Java
$OMQ_DIR/jar/qorus-common.jar: (deprecated) the base Qorus API common to all interfaces
$OMQ_DIR/jar/qorus-job.jar: (deprecated) the Qorus job API
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; most Qorus APIs require the thread context to be set up properly before calling; only services support background threads; to start a thread in a service, use the qore.OMQ.UserApi.ServiceApi.startThread() API
- 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 QoreObjectBase class (in manually-generated wrapper classes also 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.
- See also
- qore.OMQ.UserApi.UserApi.startCapturingObjectsFromJava()
- qore.OMQ.UserApi.UserApi.stopCapturingObjectsFromJava()
Writing Tests in Java for Qorus Interfaces
The following Qore classes should be dynamically imported to write tests written in Java:
- qoremod.QorusInterfaceTest.QorusWorkflowTest
- qoremod.QorusInterfaceTest.QorusServiceTest
- qoremod.QorusInterfaceTest.QorusJobTest
- Note
qoremod allows Qore's compiler and class loader to load Qore modules dynamically and then import any symbols automatically using dynamic bytecode generation for wrapper classes and other definitions
Examples of using dynamic APIs with Qorus:
# compile:
qjavac -cp ${OMQ_DIR}/jar/qore-jni.jar MyTest.java
# run:
java -Djava.library.path=$OMQ_DIR/lib/libqore.so -cp ${OMQ_DIR}/jar/qore-jni.jar:. -Djava.system.class.loader=org.qore.jni.QoreURLClassLoader MyTest- See also
-
- Example Workflow Test
import qoremod.QorusInterfaceTest.*;
import qoremod.QorusClientBase.*;
import qoremod.QorusClientBase.OMQ.$Constants;
import qoremod.QorusClientBase.OMQ.Client.QorusLocalRestHelper;
public static void main(String[] args) throws Throwable {
new TestWorkflow(args);
}
public TestWorkflow(String[] args) throws Throwable {
super("SIMPLETEST", "1.0", args);
addTestCase("wf test", new QoreClosureMarkerImpl() {
public Object call(Object... args) throws Throwable { testWorkflow(); return null; }
});
main();
}
@SuppressWarnings("unchecked")
private void testWorkflow() throws Throwable {
QorusLocalRestHelper
qrest =
new QorusLocalRestHelper();
{
{
}
});
}
});
order_hash.put("staticdata", static_data);
assertEq(qoremod.QorusClientBase.OMQ.$Constants.StatComplete, result.get("status"));
try {
int wfiid = Integer.parseInt(
omqclient.createWorkflowInstanceName(
"SIMPLETEST",
"1.0",
null,
new Hash()));
assertTrue(name != null);
qrest.
put(
"workflows/SIMPLETEST/reset");
assertEq(qoremod.QorusClientBase.OMQ.$Constants.StatComplete,
qrest.
get(
"orders/" + wfiid +
"/workflowstatus"));
} finally {
qrest.
put(
"workflows/SIMPLETEST/setAutostart?autostart=0");
}
}
}
ensure a workflow is running
Definition QorusInterfaceTest.qm:1309
auto get(*hash< auto > opt, string path, auto args, *hash< auto > hdr, *reference< hash< auto > > info)
issues an HTTP GET REST request against the server and returns the deserialized response
auto put(*hash< auto > opt, string path, auto args, *hash< auto > hdr, *reference< hash< auto > > info)
issues an HTTP PUT REST request against the server and returns the deserialized response
This class implements the client interface to a Qorus server.
Definition QorusClientBase.qm:1171
Workflow test class.
Definition QorusInterfaceTest.qm:267
hash< auto > execSynchronous(hash< auto > orderdata)
creates and executes a synchronous workflow order and returns the hash data result
This class will wait for a workflow order to reach a final status (COMPLETE, ERROR,...
Definition QorusInterfaceTest.qm:1259
nothing exec(string command)
OMQ::Client::QorusSystemRestHelper qrest
global object for accessing the Qorus REST API; initialized in QorusClient::initFast()
OMQ::QorusClientAPI omqclient
global client API variable; initialized in QorusClient::initFast()
auto put(string path, auto args, *hash< auto > hdr, *reference< hash< auto > > info)
executes a PUT call on the remote REST service and returns the response
string getQorusInstanceName()
tests whether some Qorus instance is running and returns its name
- Example Service Test
import qoremod.QorusInterfaceTest.*;
import qoremod.QorusClientBase.*;
import qoremod.QorusClientBase.OMQ.$Constants;
import qoremod.QorusClientBase.OMQ.Client.QorusSystemRestHelper;
public static void main(String[] args) throws Throwable {
new TestService(args);
}
public TestService(String[] args) throws Throwable {
super("http-test");
addTestCase("svc test", new QoreClosureMarkerImpl() {
public Object call(Object... args) throws Throwable { testService(); return null; }
});
main();
}
private void testService() throws Throwable {
assertEq(1, ((Object[])call.
getResult())[0]);
}
}
Call any of Qorus services.
Definition QorusInterfaceTest.qm:1216
auto getResult()
Returns anything that the service method returned in the run() call.
service test class
Definition QorusInterfaceTest.qm:432
- Example Job Test
import qoremod.QorusInterfaceTest.*;
import qoremod.QorusClientBase.*;
import qoremod.QorusClientBase.OMQ.$Constants;
import qoremod.QorusClientBase.OMQ.Client.QorusSystemRestHelper;
public static void main(String[] args) throws Throwable {
new TestJob(args);
}
public TestJob(String[] args) throws Throwable {
super("test");
addTestCase("job test", new QoreClosureMarkerImpl() {
public Object call(Object... args) throws Throwable { testJob(); return null; }
});
main();
}
private void testJob() throws Throwable {
action =
new RunJobResult(qoremod.QorusClientBase.OMQ.$Constants.StatComplete);
}
}
job test class
Definition QorusInterfaceTest.qm:502
runs a job and compares the result status and optionally job instance information
Definition QorusInterfaceTest.qm:1454
run the job
Definition QorusInterfaceTest.qm:1425
Qorus Java Client API
The Qorus client API used in Java should be dynamically imported; ex:
# compile:
qjavac -cp ${OMQ_DIR}/jar/qore-jni.jar MyQorusClient.java
# run:
java -Djava.library.path=$OMQ_DIR/lib/libqore.so -cp ${OMQ_DIR}/jar/qore-jni.jar:. -Djava.system.class.loader=org.qore.jni.QoreURLClassLoader MyQorusClientSee 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