6.3.5. Web Service Client API¶
Client API is the name of the API offered to the end users of JQM: it allows to interact with running jobs, offering operations such as creating a new execution request, cancelling a request, viewing all currently running jobs, etc. Read client API before this chapter, as it gives the definition of many terms used here as well as the general way to use clients.
The main client API is the Hibernate Client API, which runs directly against the JQM central database. As JQM is database centric, finding jobs to run by polling the database, this is most efficient. However, this API has a serious drawback: it forces the user of the API to use Hibernate. This can be a huge problem in EE6 applications, as most containers (WebSphere, Glassfish, JBoss…) offer their own implementation of the JPA standard which is not compatible with Hibernate and cannot coexist with it (there must be only one JPA implementation at the same time, and a database created for Hibernate is very difficult to reuse in another JPA provider). Moreover, even outside the EE6 field, the client may already have chosen a JPA implementation that is not Hibernate. This is why JQM also offers an optional REST Web Service Client API.
6.3.5.1. Client side¶
There are two ways to use the WS Client API:
- Using the Java client
- Directly using the web service
6.3.5.1.1. Using the Java client¶
This is a standard client API implementing the JqmClient interface. Like all clients, it is used by putting its jar on your classpath. The client uses the JAX-RS 2 API, so it needs an implementation such as Jersey (obviously not provided, the one provided by the container will be used).
For Maven users:
<dependency>
<groupId>com.enioka.jqm</groupId>
<artifactId>jqm-api-client-jersey</artifactId>
<version>${jqm.version}</version>
</dependency>
and then using the API:
JqmClient jqm = JqmClientFactory.getClient();
As with any client (see the JavaDoc) clients are cached by the API, so it is not necessary to cache them yourself.
Interrogating the API is then also exactly the same as with any other client. For example, to list all running jobs:
JqmClientFactory.getClient().getJobs()
The specific parameters are:
Name | Compulsory | Description | Example |
---|---|---|---|
com.enioka.jqm.ws.url | YES | The base URL of the web service | http://localhost:1789/api/ws |
com.enioka.jqm.ws.login | If auth is used only. | mylogin | |
com.enioka.jqm.ws.password | if login | password | |
com.enioka.jqm.ws.keystoreFile | if CSA | Store for client certificates authentication | ./conf/client.pfx |
com.enioka.jqm.ws.keystoreType | Type of the previous store | PKCS12 | |
com.enioka.jqm.ws.keystorePass | Password of the store | MyPassword | |
com.enioka.jqm.ws.truststoreFile | if SSL | Trust roots for server certificates | ./conf/client.pfx |
com.enioka.jqm.ws.truststorePass | Password of the store | NoPassword |
and can be set:
(specific to this client) with a jqm.properties file inside the META-INF directory
(as for every other client) using Java code, before creating any client:
Properties p = new Properties(); p.put("com.enioka.jqm.ws.url", "http://localhost:9999/marsu/ws"); JqmClientFactory.setProperties(p);
through a system parameter (-Dcom.enioka.jqm.ws.url=http://…)
6.3.5.1.2. Interrogating the service directly¶
The previous client is only a use of the JAX-RS 2 API. You can also create your own web service proxy by interrogating the web service with the library of your choice (including the simple commons-http). See the Service reference for that.
Should that specific implementation need the interface objects, they are present in the jqm-api-client jar (the pure API jar without any implementation nor dependencies).
<dependency>
<groupId>com.enioka.jqm</groupId>
<artifactId>jqm-api-client</artifactId>
<version>${jqm.version}</version>
</dependency>
6.3.5.1.3. Choosing between the two approaches¶
When using Java, the recommended approach is to use the provided client. This will allow you to:
- ignore completely all the plumbing needed to interrogate a web service
- change your client type at will, as all clients implement the same interface
- go faster with less code to write!
The only situations when it is recommended to build your own WS client are:
- when using another language
- when you don’t want or can’t place the WS client library Jersey on your classpath. For example, in an EE6 server that provides JAX-RS 1 and just don’t want to work with version 2.
6.3.5.2. Server side¶
The web service is not active on any engine by default. To activate it, see the administration guide.
It is not necessary to enable the service on all JQM nodes. It is actually recommended to dedicate a node that will not host jobs (or few) to the WS. Moreover, it is a standard web application with purely stateless sessions, so the standard mechanisms for load balancing or high availability apply if you want them.
6.3.5.3. Service reference¶
All objects are serialized to XML by default or to JSON if required. The service is a REST-style web service, so no need for SOAP and other bubbly things.
URL pattern | Method | Non-URL arguments | Return type | Return MIME | Interface equivalent | Description |
---|---|---|---|---|---|---|
/ji | GET | List<JobInstance> | application/xml | getJobs | List all known job instances | |
/ji | POST | JobRequest | JobInstance | application/xml | enqueue | New execution request |
/ji/query | POST | Query | Query | application/xml | getJobs(Query) | Returns the executed query |
/ji/{jobId} | GET | JobInstance | application/xml | getJob(int) | Details of a Job instance | |
/ji/{jobId}/messages | GET | List<String> | application/xml | getJobMessages(int) | Retrieve messages created by a Job Instance | |
/ji/{jobId}/files | GET | List<Deliverables> | application/xml | getJobDeliverables | Retrieve the description of all files created by a JI | |
/ji/{jobId}/stdout | GET | InputStream | application/os | getJobLogStdOut | Retrieve the stdout log file of the (ended) instance | |
/ji/{jobId}/stderr | GET | InputStream | application/os | getJobLogStdErr | Retrieve the stderr log file of the (ended) instance | |
/ji/{jobId}/position/{} | POST | void | setJobQueuePosition | Change the position of a waiting job instance inside a queue. | ||
/ji/active | GET | List<JobInstance> | application/xml | getActiveJobs | List all waiting or running job instances | |
/ji/cancelled/{jobId} | POST | void | cancelJob(int) | Cancel a waiting Job Instance (leaves history) | ||
/ji/killed/{jobId} | POST | void | killJob(int) | Stop (crashes) a running job instance if possible | ||
/ji/paused/{jobId} | POST | void | pauseQueuedJob(int) | Pause a waiting job instance | ||
/ji/paused/{jobId} | DELETE | void | resumeJob(int) | Resume a paused job instance | ||
/ji/waiting/{jobId} | DELETE | void | deleteJob(int) | Completely cancel/remove a waiting Job Instance (even history) | ||
/ji/crashed/{jobId} | DELETE | JobInstance | application/xml | restartCrashedJob | Restarts a crashed job instance (deletes failed history) | |
/q | GET | List<Queue> | application/xml | getQueues | List all queues defined in the JQM instance | |
/q/{qId}/{jobId} | POST | void | setJobQueue | Puts an existing waiting JI into a given queue. | ||
/user/{uname}/ji | GET | List<JobInstance> | application/xml | getActiveJobs | List all waiting or running job instances for a user | |
/jd | GET | List<JobDef> | application/xml | getActiveJobs | List all job definitions | |
/jd/{appName} | GET | List<JobInstance> | application/xml | getActiveJobs | List all job definitions for a given application | |
/jr | GET | JobRequest | application/xml | N/A | Returns an empty JobRequest. Usefull for scripts. |
Note: application/os = application/output-stream.
Used HTTP error codes are:
- 400 (bad request) when responsibility for the failure hangs on the user (trying to delete an already running instance, instance does not exist, etc)
- 500 when it hangs on the server (unexpected error)
On the full Java client side, these are respectively translated to JqmInvalidRequestException
and JqmClientException
.
The body of the response contains an XML or JSON item giving details on the error.:
1 404 GET on absent object
2 500
3 404 DELETE on absent object
4 400 Update user with absent role
5 500 Could not create certificate
6 400 Invalid enqueue parameters
7 400 Simple API is only available when the application runs on top of JQM itself and not a web application server.
8 400 File does not exist
9 500 Generic internal exception
10 400 Generic bad request
6.3.5.4. Script sample¶
PowerShell script. Logics is the same in any language, script or compiled.:
# Note: we use JSON as a demonstration of how to use it over the default XML. Obviously, PowerShell deals with XML very well and does not need this.
# Authentication?
$cred = Get-Credential root
#################################
## Enqueue demonstration
#################################
$request = @{applicationName="DemoApi"; user=$env:USERNAME} | ConvertTo-Json
$jobInstance = Invoke-RestMethod http://localhost:62948/ws/client/ji -Method Post -Body $request -Credential $cred -ContentType "application/json" -Headers @{Accept="application/json"}
$jobInstance.id
#################################
## Query demonstration
#################################
$query = @{applicationName="DemoApi"} | ConvertTo-Json
$res = Invoke-RestMethod http://localhost:62948/ws/client/ji/query -Method Post -Body $query -Credential $cred -ContentType "application/json" -Headers @{Accept="application/json"}
$res.instances | Format-Table -AutoSize