Headless API
Headless Client Plugin Guide / Headless API
Overview
The Headless API allows you to create and manage headless clients dynamically. These clients can be started from other sessions to offload heavy tasks, allowing for parallel processing. This is especially useful for long-running jobs or tasks that would block normal operations in a single-threaded environment.
You can find more detailed information about Headless API here and about client methods here.
Features
Parallel Processing: Servoy clients are single-threaded, meaning they can only perform one task at a time. The Headless API provides a way to offload long-running tasks to another session.
Dynamic Client Creation: You can create headless clients programmatically, start, monitor, and shut them down as needed.
Job Queuing and Monitoring: Jobs can be queued for execution, and their progress can be monitored through callbacks or status checks.
Creating a client
To create a headless client, you will typically use the createClient method. This method opens a new session of a specified solution on the server, allowing you to interact with it programmatically.
Example: Creating a Headless Client
Getting a running client
To retrieve a running headless client in Servoy, you can use the getClient or getOrCreateClient methods from the plugins.headlessclient API. These methods allow you to access an existing headless session, enabling interaction with the client that’s already running on the server.
getClient
To retrieve a running headless client using the getClient(clientID) method, you need the clientID
that uniquely identifies the headless client session. This method is helpful when you already have an active headless client on the server, and you want to reconnect to it to perform further operations.
This method does not create a new headless client—if the specified client ID doesn’t exist or the session has been closed, the method will return null
.
Use Case for getClient This method is particularly useful when:
You have already created a headless client, and the process needs to be resumed or continued later.
You want to queue additional tasks or check the status of a long-running headless session.
The client session has to be accessed across different parts of the application or across different user sessions.
For instance, you might create a headless client to perform batch processing, and after some time, you retrieve it using its clientID to check if the task is complete or to perform more actions.
Example: Retrieving a Running Headless Client
getOrCreateClient
The getOrCreateClient method in Servoy is used to either retrieve an existing headless client using its unique clientID
or create a new one if no client with the specified ID is found. This method is ideal when you need to ensure that a headless client session is available, whether it already exists or needs to be created.
If a headless client with the specified clientID exists and is running with the provided solution, it will return that client. If no such client exists, the method will create a new headless client for the specified solution and return it.
Use Case for getOrCreateClient This method is particularly useful when:
You are uncertain whether a specific headless client is already running, but you want to ensure that the client is either retrieved or created to perform some tasks.
You want to persist a specific session across different operations (e.g., for background processes, integration tasks, or automation) by reusing a clientID.
You need a long-running or reusable headless client session that can be accessed and shared by different parts of your solution or after a server restart.
Example: Retrieving or Creating a Headless Client
Difference between getClient
and getOrCreateClient
getClient
and getOrCreateClient
The key difference between getClient
and getOrCreateClient
lies in their behavior when a headless client with the specified clientID does not exist. Here’s a detailed breakdown of how each method operates:
getClient
Purpose: The
getClient
method is used solely to retrieve an existing headless client.Behavior:
If a headless client with the specified
clientID
is running, it returns that client.If no client with the given
clientID
is found, it returns null.
Use Case: Use
getClient
when you know that a headless client already exists, and you want to reconnect or continue interacting with it. This method does not create a new client if the specified one doesn't exist.
getOrCreateClient
Purpose: The
getOrCreateClient
method can either retrieve an existing client or create a new one.Behavior:
If a headless client with the specified
clientID
already exists for the specified solution, it returns that client.If no client with the given
clientID
exists, it creates a new headless client using the providedsolutionName
,username
,password
, andsolutionOpenMethodArgs
.
Use Case: Use
getOrCreateClient
when you want to ensure that a client exists for the givenclientID
. If it’s not running already, this method will create a new one, making it useful in situations where you want to avoid checking whether a client exists before creating it.
Key Differences:
Reusing a fixed clientID vs creating a unique (UUID)
When working with Servoy headless clients, you have the option to either reuse a fixed clientID or generate a unique clientID (UUID) for each new client session. Both approaches have their advantages and use cases depending on the nature of your application, the longevity of the headless client sessions, and how you intend to interact with those sessions.
Reusing a Fixed clientID
Overview:
In this approach, you use a predefined, fixed clientID for a specific headless client session.
The same
clientID
is reused across multiple sessions or interactions, ensuring that the same headless client session can be retrieved and continued over time.
Benefits:
Session Continuity: By reusing a fixed
clientID
, you ensure that the headless client session can be persisted and accessed repeatedly, even after restarts. This is ideal for long-running processes where the client must be revisited later to resume operations or access its state.Resource Efficiency: Reusing a fixed
clientID
prevents creating multiple headless clients for the same task. Instead of starting a new session each time, you can retrieve and reuse the existing client, which saves server resources.State Preservation: If your headless client maintains important state or context (such as working with foundsets, data processing, etc.), using a fixed clientID allows you to retrieve that session and continue where you left off.
Use Case:
Ideal for long-running processes that may need to be resumed over time (e.g., batch data processing, scheduled tasks, or background jobs).
Useful when multiple systems or components need to interact with the same session or share client data.
Considerations:
Single-Session Limitation: If the same
clientID
is reused in multiple places or by different users, it can lead to conflicts or unexpected behavior, as there will only be one session associated with that ID.Concurrency: Only one instance of the headless client will be running for the given
clientID
, which could be a limitation if multiple processes need to run independently.
Example:
Creating a Unique clientID (UUID)
Overview:
In this approach, you generate a unique identifier (UUID) for each new headless client session. UUIDs are universally unique and ensure that every client session is completely independent from others.
This is usually done by creating a new
clientID
each time a headless client is instantiated, ensuring that no session collides with another.
Benefits:
Isolation of Sessions: Each headless client gets its own unique session, which prevents any interference between different processes. This is important when tasks need to run independently without affecting each other.
Concurrency: You can run multiple independent headless clients in parallel, each with its own UUID, enabling concurrent processing. Each session will have its own state, and multiple clients can perform tasks simultaneously without conflict.
Automatic Cleanup: If your system doesn’t require long-term persistence of the client session, using UUIDs ensures that each session is isolated and can be easily discarded when no longer needed.
Use Case:
Ideal for short-lived or one-off tasks that don’t need to persist across sessions (e.g., processing user-specific requests, running temporary background tasks, handling concurrent jobs).
Useful when you need to run multiple, independent headless client sessions simultaneously.
Considerations:
Resource Usage: Since each UUID generates a new client session, you might use more server resources as multiple headless clients are created and managed simultaneously.
No Continuity: Unlike fixed clientIDs, sessions created with a UUID typically don’t have long-term persistence. Once the session ends, the client is usually discarded, meaning you cannot revisit that session later.
Session Management: You need to ensure that unused sessions are properly cleaned up (e.g., using
shutdown()
), especially in cases where many UUIDs are generated.
Example:
Summary of Key Differences:
When to Use:
Reusing a fixed clientID is best when you need session persistence and plan to revisit the same client session over time. It is ideal for background processes, long-running tasks, or shared headless sessions.
Creating a unique clientID (UUID) is useful when tasks need to run independently, or you are handling short-lived, one-time processes. It is perfect for cases where multiple clients need to run concurrently, with each having its own isolated session.
Running/scheduling on startup
Running or scheduling headless clients on startup in Servoy can be an effective way to initiate background processes, such as data synchronization, batch processing, or scheduled tasks, as soon as the application server or solution starts. Here’s how you can automate headless client operations to run on startup.
Running a headless client on startup
The simplest way to run a headless client on startup is by placing the headless client creation logic inside the solution's onSolutionOpen
method. This method runs automatically when the solution starts and can be used to kick off background processes.
Example: Starting a Headless Client in the onOpen Method
In this approach:
A headless client is created automatically when the solution starts.
The
initializeBackgroundTasks
method is queued for execution by the headless client.
Scheduling a headless client on startup
If you want to schedule headless clients to run at regular intervals (including on startup), you can use the Scheduler Plugin. You can schedule a headless client to start immediately after the solution loads, or at a specific time.
Example: Scheduling a Headless Client with the Scheduler
In this approach:
A cron job is scheduled using addCronJob, which will trigger the
startHeadlessClientOnStartup
function at the specified time (in this case, hourly).The headless client is created within the scheduled job, and a method is queued for execution.
Queuing a job
Queuing a job in Servoy, especially within a headless client, is a common task used to perform background operations without interrupting the main application flow. The method queueMethod() is used to queue jobs for headless clients, allowing for asynchronous execution of server-side logic, such as data processing, external API calls, or other long-running tasks.
When multiple jobs are queued, they will be executed sequentially in the order they are added to the queue.
Example: Queuing Multiple Jobs
Steps in the Example:
Create a Headless Client: The headless client is created using createClient. If you have an existing client, you can use getClient or getOrCreateClient to retrieve it.
Queue Multiple Jobs: The queueMethod() is used to queue multiple methods (
processJob1
,processJob2
,processJob3
) for execution on the server. Each method is passed a set of parameters (in this case,param1
,param2
,param3
). No callback is provided, meaning the job runs in the background, and the result or any exceptions are not tracked.Execution Order: Jobs are executed in the order they are queued. In this case:
processJob1
will execute first.After that,
processJob2
will run.Finally,
processJob3
will execute.
Optional Logging: Logging the job queue status with
application.output()
helps track that the jobs were queued successfully.
Example: Queuing Jobs with Complex Parameters If the methods require complex parameters (e.g., objects or arrays), you can pass them in the queueMethod() call:
Handling the callback
Handling the callback in Servoy when queuing jobs in a headless client is important when you need to track the result of the background task, manage errors, or take further actions based on the outcome of the job. The queueMethod() function allows you to define a callback method that is triggered once the job is completed, giving you the flexibility to manage success or handle any exceptions that occur during execution.
When queuing a method using queueMethod(), you can provide a callback function that will be invoked after the method completes its execution on the server. The callback function receives a JSEvent object that provides information about the execution, including whether it succeeded or failed.
Example: Handling the Callback
Key Components:
Queuing the Job:
The queueMethod() method is used to queue the
processJob
method for execution.The last argument in the method is the
jobCallback
function, which will be invoked once the job is completed.
The Callback Function:
The callback function receives a JSEvent object.
You can use event.getType() to check if the job completed successfully or if an error occurred.
There are two possible outcomes:
JSClient.CALLBACK_EVENT: This indicates that the method executed successfully. The result (if any) is contained in
event.data
.JSClient.CALLBACK_EXCEPTION_EVENT: This indicates that an error occurred during the execution. The error details are in
event.data
.
Handling Success:
When the job completes successfully, event.getType() will return JSClient.CALLBACK_EVENT.
You can access the result of the method (if any) through
event.data
.
Handling Errors:
If an exception occurs during the job execution, event.getType() will return JSClient.CALLBACK_EXCEPTION_EVENT.
The error message or exception details will be available in
event.data
.You can log the error or trigger corrective actions based on this information.
Example: Handling Multiple Jobs with Different Callbacks You can queue multiple jobs with different callback functions to handle each job’s result individually.