Orchestrator for ScriptX.Services for Windows PC
Introduction
ScriptX.Services for Windows PC does not run as a system service, it runs per user.
ScriptX.Services Orchestrator for Windows PC (Orchestrator) assists in the case of multiple users concurrently signed in to a Windows PC with ScriptX.Services for Windows PC running for each user.
If the PC is not shared, or all users always sign out at the end of their session then Orchestrator is not helpful because all users will use the same port (typically the default port 41191).
ScriptX.Services Orchestrator for Windows PC does not assist ScriptX.Services for On Premise Devices or ScriptX.Services for Cloud.
What happens without Orchestrator
Since v2.15.1 as each user signs in, concurrently with other users, ScriptX.Services for Windows PC starts up and assigns an unique port to itself. The port number is assigned as the next available port after the configured default port (for first run this is 41191 typically).
So, the first user to sign in will use port 41191, the second port 41192, the third port 41193 and so on. The port used is recorded in the appsettings.json
file for the user
(typically located at C:\Users\[UserName]\AppData\Local\MeadCo\ScriptXServices
) and will be used as the configured default port the next time the user signs in.
As described above, if that port is in use, the next available port will be used.
So far, so good. The issue now is that javascript code in the client browser is likely to be attempting to use the default port for connecting to the server (http://127.0.0.1:41191) and that port will be wrong for all users after the first.
There are 3 possible solutions to this problem :
-
Dynamically generate the client side code to use the correct port for the user. To make this work effectively most likely requires that the port for a user is recorded in the user's profile data held in the application and is also specified in the
appsettings.json
file for the user (typically located at C:\Users\[UserName]\AppData\Local\MeadCo\ScriptXServices).Alternatively it is possible to store the port used in the user's browser local storage or a cookie.
Either may be complex to code and maintain.
-
If using the ScriptX.Services v2.15 and later and v1.9 or later of the ScriptX.Services Client Library (implements compatibility to ScriptX.Addon for ScriptX.Services) then the library will take the given server url, including port and try to connect to it.
If the connection fails it will increment the port and try the next, trying up to 10 ports.
This can be a slow process that may have a significant negative effect on the user's experience.
-
Use Orchestrator for ScriptX.Services for Windows PC.
Orchestrator provides a fast response with the ScriptX.Services for Windows PC port allocated to the user from a single endpoint that is the same for all users. The api is simple to use and does not negatively effect the user's experience.
Orchestrator is available in two forms:
-
Built-in with ScriptX.Services for Windows PC v2.17.0 and later.
To work with built-in Orchestrator either use v1.10 or later of ScriptX.Services Client Library or use the Orchestrator API in your own code.
This solution is recommended for single Windows 10/11 PCs shared among several users.
-
As a 1st class Windows system service installed in addition to ScriptX.Services for Windows PC.
ScriptX.Services for Windows PC 2.18.0 or later is required and v1.12 or later of the ScriptX.Services Client Library or use the Orchestrator API in your own code.
This solution is required when using a shared host in desktop experiences such as RDS.
This solution can also be used for single Windows 10/11 PCs shared among several users. This enables a single code base solution that is applicable to different environments.
-
What happens with Orchestrator Built-in
Orchestrator is built-in and available with ScriptX.Services for Windows PC v2.17.0 and later. When a user signs in, Orchestrator starts up and listens on the configured port (typically 41190).
The way that ScriptX.Services for Windows PC works does not change; as each user signs in, ScriptX.Services for Windows PC starts up and assigns an unique port to itself.
When the user becomes inactive, for example because another user is signing in, their Orchestrator instance stops listening on the configured port. This makes the same port available to the new active user - where their own Orchestrator instance starts up and listens on the configured port (typically 41190).
Orchestrator provides an API callable by the client browser javascript code that returns the port used by ScriptX.Services for Windows PC for the current user.
This is a single, quick, call that will not negatively impact the user's experience.
What happens with Orchestrator as a Service
Each instance of ScriptX.Services for Windows PC, in other words each user, requires a unique key value. The key to use must be included in the configuration for ScriptX.Services for Windows PC and may be configured as the user's Windows signin name by using the value [username]
.
The key value is used to register the port in Orchestrator, then client script can obtain the port registered to the key (user).
-
Orchestrator as a Service starts when the OS boots and listens on the configured port (typically 41190)
-
When a user signs in the way that ScriptX.Services for Windows PC works does not change except for the addition noted below; as each user signs in, ScriptX.Services for Windows PC starts up and assigns an unique port to itself.
However, in addition, ScriptX.Services for Windows PC calls the Orchestrator as a Service API to record the port in use for the key value.
If the user accesses the ScriptX.Services for Windows PC dashboard they will be asked to enter their keyvalue so that the correct dashboard is displayed.
Orchestrator as a Service provides an API callable by the client browser javascript code that returns the port used by ScriptX.Services for Windows PC registered for a key value.
This is a single, quick, call that will not negatively impact the user's experience.
The client script must have the correct key value to use. This means the application must have a means of identifying the user and making the key value available to the client javascript code.
Using Built-in Orchestrator
As Orchestrator is built-in there is no installation requirement - it is "installed" with ScriptX.Services for Windows PC.
With ScriptX.Services Client Library
Please note that in the examples below 3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497 should be replaced with your own License GUID.
Using attributes to specify the connection to ScriptX.Services
Specify that Orchestrator should be used by adding the data-meadco-orchestrator attribute with a value of the port that Orchestrator is using (note that Orchestrator will only be called on http://127.0.0.1), for example:
<!-- Add.on to scriptx.services compatibility -->
<script src="//cdn.jsdelivr.net/npm/scriptxprint-html@1/dist/meadco-scriptxservices.min.js"
data-meadco-server="http://127.0.0.1:41191"
data-meadco-orchestrator="41190"
data-meadco-license="3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497"
data-meadco-license-path="warehouse"
data-meadco-license-revision="0"
data-meadco-syncinit="false"
>
</script>
Using code to specify the connection
Set the "MeadCo.ScriptX.Print.orchestrator" property to a non-zero value to enable use of Orchestrator, the value is the port used by Orchestrator. This must be done before calls to connect to ScriptX.Services or apply a license. For example:
'use strict';
MeadCo.ScriptX.Print.orchestrator = 41190;
MeadCo.ScriptX.Print.Licensing.connect("http://127.0.0.1:41191","{3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497}");
MeadCo.ScriptX.Print.Licensing.apply(
"{3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497}",0,"warehouse");
MeadCo.ScriptX.Print.HTML.connect(
"http://127.0.0.1:41191","{3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497}");
With "fetch" based code
The API is simple and requires no authentication. Default fetch() options should work.
It would be reasonable to assume that if the call fails then Orchestrator ScriptX.Services is not available or not running for some reason rather than there having been an error at the server.
// 'private' connection data
//
let _serviceUrl = "";
let _licenseGuid = "";
// apiEndPoint
//
// construct and return url for api
//
function apiEndPoint(apiName) {
return _serviceUrl + "api/v1/" + apiName;
}
// Find the port in use by Script.Services for Windows PC for the current user
// by asking Built-in Orchestrator.
//
// Returns the port number or 0 on error.
async function getScriptXServicesPort() {
let port = 0;
try {
let response = await fetch("http://127.0.0.1:41190/api/v1");
if (response.ok) {
let json = await response.json();
port = json.HttpPort;
}
else {
console.error("Error in request to Orchestrator: " + response.status);
}
}
catch (error) {
console.error("Error in request to Orchestrator: ", error);
}
return port;
}
// headers
//
// returns the http header including to authorize access to ScriptX.Services
//
function headers() {
return {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa(_licenseGuid + ':')
}
}
// parseResponse
//
// If the reponse from fetch is not ok, throw the text of the error
// If ok, return the json object delivered from the server.
//
async function parseResponse(response) {
if (!response.ok) {
throw Error(await response.text());
}
return await response.json();
}
// callService
//
async function callService(apiName, sMethod, msg) {
const response = await fetch(apiEndPoint(apiName), {
method: sMethod,
cache: 'no-cache',
headers: headers(),
body: msg ? JSON.stringify(msg) : null
});
return await parseResponse(response);
}
// applyPrintLicense
//
// Use the license on this page
//
// Returns the detail of the license if successful, or throws an error.
//
async function applyPrintLicense() {
return await callService('licensing', 'POST', {
"guid": _licenseGuid,
"url": "warehouse",
"revision": 0
});
}
// export: connectToService
//
// record server to use and apply the client license to this page
//
// Returns the detail of the license if successful, or throws an error.
//
async function connectToServiceViaOrchestrator(sLicenseGuid) {
let p = await getScriptXServicesPort();
if ( p<=0 ) p = 41191;
_serviceUrl = "http://127.0.0.1:" + p + "/";
_licenseGuid = sLicenseGuid;
return await applyPrintLicense();
}
response is port: not run yet (0 means failure, see console for reasons).
Using Orchestrator as a Service
Prepare configuration
We recommend strongly that system environment values for all users are used as these are shared between Orchestrator and ScriptX.Services for all users hence minimising management and maintenance effort.
The simplest configuration is to use the user's Windows user name as the key:
Name | Value |
---|---|
MEADCOSCRIPTX_ServerHost__OrchestratorKey | [username] |
MEADCOSCRIPTX_Orchestrator__Endpoints__Service__Port | 41190 |
System environment variables and values can be created and edited using the System Properties dialog (in Windows 10/11 search for "Edit environment variables" or "Edit the system environment").
Alternatively the following can be used in command file run from an Administrator command prompt:
SETX MEADCOSCRIPTX_ServerHost__OrchestratorKey [username] /M
SETX MEADCOSCRIPTX_Orchestrator__Endpoints__Service__Port 41190 /M
See Installing ScriptX.Services for Windows PC for further information on configuration and options
After editing the environment restart the PC so that the values are available.
Download and install
Orchestrator as a Service must be downloaded and installed separately from ScriptX.Services for Windows PC.
To download or upgrade to the latest version (2.2.1.4) of Orchestrator as a Service for ScriptX.Services for Windows PC, download and run the installer.
In order for the installer to run, you must be logged on to your computer as an Administrator.
If required upgrade ScriptX.Services for Windows PC v2.18.0 or later.
To download or upgrade to the latest version (2.22.0.22) of ScriptX.Services for Windows PC, download and run the installer.
In order for the installer to run, you must be logged on to your computer as an Administrator.
Using Orchestrator as a Service with ScriptX.Services Client Library
Please note that in the examples below 3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497 should be replaced with your own License GUID.
Using attributes to specify the connection to ScriptX.Services
Specify that Orchestrator should be used by adding the data-meadco-orchestrator and data-meadco-orchestrator-key attributes, for example:
<!-- Add.on to scriptx.services compatibility -->
<script src="//cdn.jsdelivr.net/npm/scriptxprint-html@1/dist/meadco-scriptxservices.min.js"
data-meadco-server="http://127.0.0.1:41191"
data-meadco-orchestrator="41190"
data-meadco-orchestrator-key="My-User-Name"
data-meadco-license="3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497"
data-meadco-license-path="warehouse"
data-meadco-license-revision="0"
data-meadco-syncinit="false"
>
</script>
Using code to specify the connection
Set the "MeadCo.ScriptX.Print.orchestrator" property to a non-zero value to enable use of Orchestrator and the "MeadCo.ScriptX.Print.orchestratorKey" property. This must be done before calls to connect to ScriptX.Services or apply a license. For example:
'use strict';
MeadCo.ScriptX.Print.orchestrator = 41190;
MeadCo.ScriptX.Print.orchestratorKey = "My-User-Name";
MeadCo.ScriptX.Print.Licensing.connect("http://127.0.0.1:41191","{3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497}");
MeadCo.ScriptX.Print.Licensing.apply(
"{3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497}",0,"warehouse");
MeadCo.ScriptX.Print.HTML.connect(
"http://127.0.0.1:41191","{3cfd70e2-f38f-4ab2-95f4-4ce4c1e39497}");
Using Orchestrator as a Service with "fetch" based code
The API is simple and requires no authentication. Default fetch() options should work.
It would be reasonable to assume that if the call fails then Orchestrator ScriptX.Services is not available or not running for some reason rather than there having been an error at the server.
// Find the port in use by Script.Services for Windows PC for the current user
// by asking Orchestrator as a Service (api/v2)
//
// Returns the port number or 0 on error.
async function getScriptXServicesServicePort(key) {
let port = 0;
try {
let response = await fetch(`http://127.0.0.1:41190/api/v2?key=${key}`);
if (response.ok) {
let json = await response.json();
port = json.HttpPort;
}
else {
console.error("Error in request to Orchestrator: " + response.status);
}
}
catch (error) {
console.error("Error in request to Orchestrator: ", error);
}
return port;
}
Key:
response is port: not run yet (0 means failure, see console for reasons).