ConfigItem.java
/*
** Module : ConfigItem.java
** Abstract : Core class for resolving clientConfig and webClient configs.
**
** Copyright (c) 2024-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- --------------------------------------Description-----------------------------------------
** 001 GBB 20240624 Created initial version.
** 002 GBB 20240718 read method with all args made public.
** 003 CA 20240722 Fixed reading of Map configurations. Changed the 'clientConfig/properties' to be a real
** map.
** 004 GBB 20241010 Removing config VALIDATE_SEC_CERT (security:certificate:validate). Adding convenience
** overloading method read that accepts DirectoryService, scope and accounts.
** 005 AP 20250303 Added timeout for spawner launching.
** 006 GBB 20250403 Added SOCKET_TRUSTSTORE_FILE, SOCKET_TRUSTSTORE_PASS.
** cfgOverrides resolvement for the security context by explicit call to resolveCfgOverrides.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.util;
import com.goldencode.p2j.directory.*;
import com.goldencode.p2j.main.*;
import com.goldencode.p2j.security.*;
import com.goldencode.p2j.util.logging.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
/**
* Core class for resolving clientConfig and webClient configs.
*
* @param <T>
* The type for the value of the config.
*/
public final class ConfigItem<T>
{
/**
* Stores global data relating to the state of the current context.
*/
private static class WorkArea
{
/** Flag to indicate if the config overrides are resolved for this security context. */
private final AtomicBoolean areCfgOverridesInitialized = new AtomicBoolean(false);
/** The config overrides resolved for this security context. */
private final Map<String, String> cfgOverrides = new ConcurrentHashMap<>();
/**
* Constructor.
*/
WorkArea()
{
}
}
/** Context-local global data area for the config items. */
private static final ContextLocal<WorkArea> local = new ContextLocal<WorkArea>()
{
/**
* Initializes the work area, the first time it is requested within a
* new context.
*
* @return The newly instantiated work area.
*/
protected synchronized WorkArea initialValue()
{
return new WorkArea();
}
};
/**
* Resolve the config overrides for the specified accounts and directory scope and store them in the
* context local.
*
* @param dirScope
* The directory scope.
* @param accountIds
* The account ids for the directory lookup.
*/
public static void resolveCfgOverrides(Utils.DirScope dirScope, String[] accountIds)
{
Map<String, String> cfgOverrides = local.get().cfgOverrides;
cfgOverrides.clear();
String cfgOverridesString = ConfigItem.CFG_OVERRIDES.read(null, dirScope, accountIds);
if (cfgOverridesString != null)
{
for (String cfgOverride : cfgOverridesString.split(" "))
{
String[] configNameValue = cfgOverride.trim().split("=");
cfgOverrides.put(configNameValue[0].trim(),
configNameValue.length > 1 ? configNameValue[1].trim() : null);
}
}
local.get().areCfgOverridesInitialized.set(true);
}
/** The type of client config, based on the origin. */
public enum Type
{
/** Type for config originally in clientConfig. */
CLIENT_CONFIG,
/** Type for config originally in webClient. */
WEB_CLIENT,
/** Type for config coming from cmd line or cfgOverrides. */
CFG_OVERRIDES,
/** Type for config originating runtime. */
RUNTIME,
/** Type for config declared directly in server or account containers */
SERVER_ACCOUNT
}
/* -----------------------------------
Original clientConfig/*
----------------------------------- */
public static final ConfigItem<String> CFG_OVERRIDES = new ConfigItem<>(String.class, "cfgOverrides");
public static final ConfigItem<Boolean> SECURE =
new ConfigItem<>(Boolean.class, "secure", "net", "connection", "secure", Type.CLIENT_CONFIG);
public static final ConfigItem<String> COMMAND = new ConfigItem<>(String.class, "command");
public static final ConfigItem<String> CLASSPATH = new ConfigItem<>(String.class, "classpath");
public static final ConfigItem<String> LIB_PATH = new ConfigItem<>(String.class, "libPath");
public static final ConfigItem<String> SPAWNER = new ConfigItem<>(String.class, "spawner");
/**
* Enables spawned client process logging by the native process. Catches JVM errors. Used only for
* debugging.
*/
public static final ConfigItem<Boolean> SPAWNER_DEBUG = new ConfigItem<>(Boolean.class, "enableSpawnerDebug");
public static final ConfigItem<String> WORKING_DIR = new ConfigItem<>(String.class, "workingDir");
public static final ConfigItem<String> OUTPUT_TO_FILE =
new ConfigItem<>(String.class, "outputToFile", "server", "clientConfig", "outputToFile", Type.CLIENT_CONFIG);
public static final ConfigItem<String> CONFIG_FILE = new ConfigItem<>(String.class, "configFile");
public static final ConfigItem<String> CLASSPATH_EXTRA_JARS =
new ConfigItem<>(String.class, "classpath-extra-jars");
public static final ConfigItem<String> JVM_ARGS = new ConfigItem<>(String.class, "jvmArgs");
public static final ConfigItem<Integer> MIN_AGENT_PORT = new ConfigItem<>(Integer.class, "minAgentPort");
public static final ConfigItem<Integer> MAX_AGENT_PORT = new ConfigItem<>(Integer.class, "maxAgentPort");
/** OS user for processes appserver / scheduled batch. */
public static final ConfigItem<String> SYSTEM_USER = new ConfigItem<>(String.class, "systemUser");
/** OS pass for processes appserver / scheduled batch. */
public static final ConfigItem<String> SYSTEM_PASS = new ConfigItem<>(String.class, "systemPassword");
/** The relative desktop node path */
public static final ConfigItem<String> KEYBOARD_LAYOUTS = new ConfigItem<>(String.class, "keyboardLayouts");
public static final ConfigItem<String> KEYBOARD_LAYOUT_ID =
new ConfigItem<>(String.class, "defaultKeyboardLayoutId");
public static final ConfigItem<Boolean> KEYBOARD_SHORTCUTS_UNDERLINE =
new ConfigItem<>(Boolean.class, "underline-keyboard-shortcuts");
public static final ConfigItem<Map> HELPER_APPS = new ConfigItem<>(Map.class, "helper-applications");
public static final ConfigItem<Map> ENV_PROPERTIES = new ConfigItem<>(Map.class, "properties");
/** Launch timeout for the spawner, expressed in seconds */
public static final ConfigItem<Integer> SPAWNER_LAUNCH_TIMEOUT =
new ConfigItem<>(Integer.class,
"spawnerLaunchTimeout",
"remote",
"spawner",
"launchTimeout",
Type.CLIENT_CONFIG);
/* -----------------------------------
Original webClient/*
----------------------------------- */
public static final ConfigItem<String> PROJECT_TOKEN =
new ConfigItem<>(String.class, "project_token", "client", "project", "token", Type.WEB_CLIENT);
public static final ConfigItem<String> CFG_OVERRIDES_WEB =
new ConfigItem<>(String.class, "cfgOverrides", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> EMBEDDED =
new ConfigItem<>(Boolean.class, "embedded", "client", "web", "embedded", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> VIRTUAL_DESKTOP =
new ConfigItem<>(Boolean.class, "virtualDesktop", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> DEFAULT_OS_USER =
new ConfigItem<>(String.class, "defaultOsUser", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> OVERRIDE_OS_USER =
new ConfigItem<>(Boolean.class, "override", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> GUI_IDENTIFIER =
new ConfigItem<>(String.class, "guiIdentifier", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> CHUI_IDENTIFIER =
new ConfigItem<>(String.class, "chuiIdentifier", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> EMBEDDED_IDENTIFIER =
new ConfigItem<>(String.class, "embeddedIdentifier", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> LOGIN_TITLE =
new ConfigItem<>(String.class, "loginPageTitle", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> LOGOUT_TITLE =
new ConfigItem<>(String.class, "logoutPageTitle", "", "", "", Type.WEB_CLIENT);
/** The port for the web server on the client process. */
public static final ConfigItem<Integer> PORT =
new ConfigItem<>(Integer.class, "port", "client", "web", "port", Type.WEB_CLIENT);
/** The host for the web server on the client process. */
public static final ConfigItem<String> HOST =
new ConfigItem<>(String.class, "host", "client", "web", "host", Type.WEB_CLIENT);
public static final ConfigItem<Integer> POLL_TIMEOUT =
new ConfigItem<>(Integer.class, "pollTimeout", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Integer> CONNECTION_TEST_TIMEOUT =
new ConfigItem<>(Integer.class, "connectionTestTimeout", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Integer> CLIENT_PORT_RANGE_FROM =
new ConfigItem<>(Integer.class, "portsRange/from", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Integer> CLIENT_PORT_RANGE_TO =
new ConfigItem<>(Integer.class, "portsRange/to", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> PROXY_HOST =
new ConfigItem<>(String.class,
"portsRange/forwardedHost",
"client",
"web",
"forwardedHost",
Type.WEB_CLIENT);
public static final ConfigItem<String> PROXY_PROTOCOL =
new ConfigItem<>(String.class,
"portsRange/forwardedProto",
"client",
"web",
"forwardedProto",
Type.WEB_CLIENT);
public static final ConfigItem<String> PROXY_CLIENT_NAME_PREFIX =
new ConfigItem<>(String.class, "portsRange/namePrefix", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> PROXY_CLIENT_PATH_SEGMENT =
new ConfigItem<>(String.class, "proxyPathSegment", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> PROXY_SERVER_PATH =
new ConfigItem<>(String.class, "serverProxyPath", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> PROXY_REMOVE_SERVER_PATH =
new ConfigItem<>(Boolean.class, "removeProxyPathInWebPages", "", "", "", Type.WEB_CLIENT);
/** The "watchdogTimeout" parameter to define the watch dog timeout */
public static final ConfigItem<Integer> WATCHDOG_TIMEOUT =
new ConfigItem<>(Integer.class, "watchdogTimeout", "client", "web", "watchdogTimeout", Type.WEB_CLIENT);
/** The delay between successful MSG_PING_PONG pairs if the server replies with MSG_PING_PONG */
public static final ConfigItem<Integer> PING_PONG_INTERVAL =
new ConfigItem<>(Integer.class, "pingPongInterval", "client", "web", "pingPongInterval", Type.WEB_CLIENT);
public static final ConfigItem<Integer> MAX_LOST_PINGS =
new ConfigItem<>(Integer.class, "maxLostPings", "client", "web", "maxLostPings", Type.WEB_CLIENT);
/** The idle time after which the connection should be closed */
public static final ConfigItem<Integer> MAX_IDLE_TIME =
new ConfigItem<>(Integer.class, "maxIdleTime", "client", "web", "maxIdleTime", Type.WEB_CLIENT);
public static final ConfigItem<Integer> MAX_HTTP_IDLE_TIME =
new ConfigItem<>(Integer.class, "embWebServerMaxIdleTimeout", "client", "web", "maxHttpIdleTimeout", Type.WEB_CLIENT);
/** The "webSocketTimeout" parameter to define the maximal idle time of the configured websocket */
public static final ConfigItem<Integer> WEB_SOCKET_TIMEOUT =
new ConfigItem<>(Integer.class, "webSocketTimeout", "client", "web", "socketTimeout", Type.WEB_CLIENT);
public static final ConfigItem<Integer> CONNECT_RETRY_DELAY =
new ConfigItem<>(Integer.class,
"delayBetweenTriesToConnect",
"client",
"web",
"delayBetweenTriesToConnect",
Type.WEB_CLIENT);
public static final ConfigItem<Integer> PING_DELAY =
new ConfigItem<>(Integer.class, "delayBetweenPingTries", "client", "web", "delayBetweenPingTries", Type.WEB_CLIENT);
public static final ConfigItem<Integer> MAX_BINARY_MSG_SIZE =
new ConfigItem<>(Integer.class, "maxBinaryMessage", "client", "web", "maxBinaryMessage", Type.WEB_CLIENT);
public static final ConfigItem<Integer> MAX_TEXT_MSG_SIZE =
new ConfigItem<>(Integer.class, "maxTextMessage", "client", "web", "maxTextMessage", Type.WEB_CLIENT);
public static final ConfigItem<String> TRY_CONNECT_MSG =
new ConfigItem<>(String.class, "tryToConnectMessage", "client", "web", "tryToConnectMessage", Type.WEB_CLIENT);
public static final ConfigItem<String> SERVER_UNAVAILABLE_MSG =
new ConfigItem<>(String.class, "serverUnavailableMessage", "client", "web", "serverUnavailableMessage", Type.WEB_CLIENT);
public static final ConfigItem<String> CONNECTION_RESTORED_MSG =
new ConfigItem<>(String.class,
"connectionRestoredMessage",
"client",
"web",
"connectionRestoredMessage",
Type.WEB_CLIENT);
public static final ConfigItem<Integer> MAX_OUT_BUFFER_SIZE =
new ConfigItem<>(Integer.class, "maxOutputBufferSize", "client", "web", "maxOutputBufferSize", Type.WEB_CLIENT);
public static final ConfigItem<Integer> MAX_OUT_AGGREGATION_SIZE = new ConfigItem<>(Integer.class,
"maxOutputAggregationSize", "client", "web", "maxOutputAggregationSize", Type.WEB_CLIENT);
public static final ConfigItem<Integer> MAX_RESPONSE_HEADER_SIZE =
new ConfigItem<>(Integer.class, "maxResponseHeaderSize", "client", "web", "maxResponseHeaderSize", Type.WEB_CLIENT);
public static final ConfigItem<Integer> MAX_REQUEST_HEADER_SIZE =
new ConfigItem<>(Integer.class, "maxRequestHeaderSize", "client", "web", "maxRequestHeaderSize", Type.WEB_CLIENT);
public static final ConfigItem<String> RENDERER =
new ConfigItem<>(String.class, "renderer", "client", "gui", "renderer", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> GRAPHICS_CACHED =
new ConfigItem<>(Boolean.class, "graphicsCached", "client", "gui", "graphicsCached", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> DISABLE_PIXEL_MANIPULATION =
new ConfigItem<>(Boolean.class,
"disablePixelManipulation",
"client",
"gui",
"disablePixelManipulation",
Type.WEB_CLIENT);
public static final ConfigItem<Boolean> TASKBAR =
new ConfigItem<>(Boolean.class, "taskbar", "client", "gui", "taskbar", Type.WEB_CLIENT);
public static final ConfigItem<String> TASKBAR_STYLE =
new ConfigItem<>(String.class, "taskBarStyle", "client", "gui", "taskBarStyle", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> CLIPBOARD =
new ConfigItem<>(Boolean.class, "clipboard", "client", "web", "clipboard", Type.WEB_CLIENT);
public static final ConfigItem<Integer> ROWS =
new ConfigItem<>(Integer.class, "rows", "client", "chui", "rows", Type.WEB_CLIENT);
public static final ConfigItem<Integer> COLS =
new ConfigItem<>(Integer.class, "columns", "client", "chui", "columns", Type.WEB_CLIENT);
public static final ConfigItem<Integer> DESKTOP_HEIGHT =
new ConfigItem<>(Integer.class, "desktopHeight", "client", "gui", "desktopHeight", Type.WEB_CLIENT);
public static final ConfigItem<Integer> DESKTOP_WIDTH =
new ConfigItem<>(Integer.class, "desktopWidth", "client", "gui", "desktopWidth", Type.WEB_CLIENT);
public static final ConfigItem<String> BACKGROUND =
new ConfigItem<>(String.class, "background", "client", "chui", "background", Type.WEB_CLIENT);
public static final ConfigItem<String> FOREGROUND =
new ConfigItem<>(String.class, "foreground", "client", "chui", "foreground", Type.WEB_CLIENT);
public static final ConfigItem<String> SELECTION =
new ConfigItem<>(String.class, "selection", "client", "chui", "selection", Type.WEB_CLIENT);
public static final ConfigItem<String> FONT_NAME =
new ConfigItem<>(String.class, "fontname", "client", "chui", "fontname", Type.WEB_CLIENT);
public static final ConfigItem<Integer> FONT_SIZE =
new ConfigItem<>(Integer.class, "fontsize", "client", "chui", "fontsize", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> FORCE_CENTERED_DIALOG =
new ConfigItem<>(Boolean.class, "force-dialogs-in-center", "", "", "", Type.WEB_CLIENT);
/** Indicates whether or not pause x no message should enable the default-window */
public static final ConfigItem<Boolean> BYPASS_NO_MSG_WIN_ON_PAUSE =
new ConfigItem<>(Boolean.class, "bypass-default-window-show-in-pause-no-message", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> USE_LOCALSTORAGE =
new ConfigItem<>(Boolean.class,
"useLocalStorage",
"client",
"web",
"useLocalStorage",
Type.WEB_CLIENT);
public static final ConfigItem<Integer> BROADCAST_CHANNEL_PING_TIMEOUT =
new ConfigItem<>(Integer.class,
"broadcastChannelPingTimeout",
"client",
"web",
"broadcastChannelPingTimeout",
Type.WEB_CLIENT);
public static final ConfigItem<Boolean> ONE_BROWSER_POLICY =
new ConfigItem<>(Boolean.class, "oneBrowserPolicy", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> UPLOAD_SINGLE_FILE =
new ConfigItem<>(String.class, "uploadSingleFile", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> UPLOAD_TOTAL_FILES =
new ConfigItem<>(String.class, "uploadTotalFiles", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> MAX_FILE_SIZE_ON_DISK =
new ConfigItem<>(String.class, "fileSizeThresholdForDiskWriting", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> SPREADSHEET_ENABLED =
new ConfigItem<>(Boolean.class, "spreadsheet/enabled", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Integer> BROWSER_LOAD_TIMEOUT =
new ConfigItem<>(Integer.class, "htmlBrowser/load-timeout", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> BROWSER_CONTEXT_PATH =
new ConfigItem<>(String.class, "htmlBrowser/default-context/context-path", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<String> BROWSER_RESOURCE_BASE =
new ConfigItem<>(String.class, "htmlBrowser/default-context/resource-base", "", "", "", Type.WEB_CLIENT);
/** The placeholder is to be replaced by the name. */
public static final ConfigItem<String> BROWSER_WHITELIST_CONTEXT_PATH =
new ConfigItem<>(String.class, "htmlBrowser/whitelist/%s/context-path", "", "", "", Type.WEB_CLIENT);
/** The placeholder is to be replaced by the name. */
public static final ConfigItem<String> BROWSER_WHITELIST_RESOURCE_BASE =
new ConfigItem<>(String.class, "htmlBrowser/whitelist/%s/resource-base", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<List> LOGIN_PARAMS =
new ConfigItem<>(List.class, "loginParams", "", "", "", Type.WEB_CLIENT);
/** The placeholder is to be replaced by the name of the login param. */
public static final ConfigItem<String> LOGIN_PARAMS_REGEX =
new ConfigItem<>(String.class, "loginParamsRegex/%s", "", "", "", Type.WEB_CLIENT);
public static final ConfigItem<Boolean> CHANGE_EXPIRING_OS_PASS =
new ConfigItem<>(Boolean.class, "expiringOsPassChange", "", "", "", Type.WEB_CLIENT);
/* -----------------------------------
Configs declared directly in server or account containers
-------------------------------------- */
public static final ConfigItem<Integer> TEMP_CLIENT_POOL_SIZE =
new ConfigItem<>(Integer.class, "tempClient/poolSize", "", "", "", Type.SERVER_ACCOUNT);
public static final ConfigItem<Integer> TEMP_CLIENT_TIMEOUT =
new ConfigItem<>(Integer.class, "tempClient/timeout", "", "", "", Type.SERVER_ACCOUNT);
/* -----------------------------------
runtime / cfgOverrides / other root nodes,
not resolved from CLIENT_CONF_CONTAINER and WEB_CLIENT_CONTAINER,
not always passed as client options
-------------------------------------- */
/** The server's secure port. */
public static final ConfigItem<Integer> SERVER_SECURE_PORT =
new ConfigItem<>(Integer.class, "securePort", "net", "server", "secure_port", Type.CFG_OVERRIDES);
public static final ConfigItem<Integer> SERVER_INSECURE_PORT =
new ConfigItem<>(Integer.class, "", "net", "server", "insecure_port", Type.CFG_OVERRIDES);
public static final ConfigItem<Integer> SERVER_PORT =
new ConfigItem<>(Integer.class, "", "net", "server", "port", Type.CFG_OVERRIDES);
public static final ConfigItem<String> SERVER_HOST =
new ConfigItem<>(String.class, "", "net", "server", "host", Type.CFG_OVERRIDES);
public static final ConfigItem<Boolean> SERVER_SOCKET_NIO =
new ConfigItem<>(Boolean.class, "", "net", "socket", "nio", Type.CFG_OVERRIDES);
public static final ConfigItem<Boolean> SERVER_SSL_TRACKSEQNO =
new ConfigItem<>(Boolean.class, "", "net", "ssl", "trackSeqNo", Type.CFG_OVERRIDES);
public static final ConfigItem<String> SOCKET_TRUSTSTORE_FILE =
new ConfigItem<>(String.class, "", "ssl-socket", "truststore", "location", Type.CFG_OVERRIDES);
public static final ConfigItem<String> SOCKET_TRUSTSTORE_PASS =
new ConfigItem<>(String.class, "", "ssl-socket", "truststore", "password", Type.CFG_OVERRIDES);
public static final ConfigItem<String> LANG =
new ConfigItem<>(String.class, "lang", "client", "web", "lang", Type.RUNTIME);
public static final ConfigItem<Integer> CACHE_SIZE = new ConfigItem<>(Integer.class,
"client-cache-size",
"client",
"text-metrics",
"cache-size",
Type.RUNTIME);
public static final ConfigItem<String> DRIVER_TYPE =
new ConfigItem<>(String.class, "", "client", "driver", "type", Type.RUNTIME);
public static final ConfigItem<String> WEB_ROOT =
new ConfigItem<>(String.class, "webRoot", "client", "web", "webRoot", Type.RUNTIME);
public static final ConfigItem<String> LOGOUT_PAGE =
new ConfigItem<>(String.class, "logoutPage", "web", "url", "logoutPage", Type.RUNTIME);
public static final ConfigItem<String> LOGIN_PAGE =
new ConfigItem<>(String.class, "loginPage", "web", "url", "loginPage", Type.RUNTIME);
public static final ConfigItem<String> SERVER_BASE_URL =
new ConfigItem<>(String.class, "serverBaseUrl", "web", "url", "serverBaseUrl", Type.RUNTIME);
public static final ConfigItem<String> SSO_SUBJECT_ID =
new ConfigItem<>(String.class, "", "sso", "subject", "id", Type.RUNTIME);
public static final ConfigItem<String> SSO_STORAGE_ID =
new ConfigItem<>(String.class, "", "sso", "storage", "id", Type.RUNTIME);
public static final ConfigItem<String> SPAWNER_UUID =
new ConfigItem<>(String.class, "", "server", "spawner", "uuid", Type.RUNTIME);
/** Enables web client debug logging in js. */
public static final ConfigItem<Boolean> JS_DEBUG_LOGGING = new ConfigItem<>(Boolean.class,
"enableDebugLogging",
"client",
"web",
"enableDebugLogging",
Type.CFG_OVERRIDES);
/** Logger. */
private static final CentralLogger LOG = CentralLogger.get(ConfigItem.class);
/** The directory container name for generic client configs. */
private static final String CLIENT_CONF_CONTAINER = "clientConfig/";
/** The directory container name for web client configs. */
private static final String WEB_CLIENT_CONTAINER = "webClient/";
/** The class for the config value. */
private final Class<T> valueClass;
/** The directory node name for the config. */
private final String nodeName;
/** The {@link Type} for the config. */
private final Type type;
/** The bootstrap option full name: category:group:key. */
private final String optionName;
/** The bootstrap option category. */
private final String optionCategoryName;
/** The bootstrap option group. */
private final String optionGroupName;
/** The bootstrap option key. */
private final String optionKeyName;
/**
* Private constructor.
*
* @param valueClass
* The class for the config value.
* @param nodeName
* The directory node name for the config.
*/
private ConfigItem(Class<T> valueClass, String nodeName)
{
this(valueClass, nodeName, null, null, null, Type.CLIENT_CONFIG);
}
/**
* Private constructor.
*
* @param valueClass
* The class for the config value.
* @param nodeName
* The directory node name for the config.
* @param optionCategoryName
* The bootstrap option category.
* @param optionGroupName
* The bootstrap option group.
* @param optionKeyName
* The bootstrap option key.
* @param type
* The {@link Type} for the config.
*/
private ConfigItem(Class<T> valueClass,
String nodeName,
String optionCategoryName,
String optionGroupName,
String optionKeyName,
Type type)
{
this.valueClass = valueClass;
this.nodeName = nodeName;
this.optionCategoryName = optionCategoryName;
this.optionGroupName = optionGroupName;
this.optionKeyName = optionKeyName;
this.optionName = (optionCategoryName == null && optionGroupName == null && optionKeyName == null) ?
null :
optionCategoryName + ":" + optionGroupName + ":" + optionKeyName;
this.type = type;
}
/**
* Returns the string representation of the client config. The same as {@link #name}.
*
* @return See above.
*/
@Override
public String toString()
{
return nodeName;
}
/**
* Getter for {@link #name}.
*
* @return See above.
*/
public String name()
{
return nodeName;
}
/**
* Getter for {@link #optionCategoryName}.
*
* @return See above.
*/
public String category()
{
return optionCategoryName;
}
/**
* Getter for {@link #optionGroupName}.
*
* @return See above.
*/
public String group()
{
return optionGroupName;
}
/**
* Getter for {@link #optionKeyName}.
*
* @return See above.
*/
public String key()
{
return optionKeyName;
}
/**
* Getter for {@link #optionName}.
*
* @return See above.
*/
public String fullOption()
{
return optionName;
}
/**
* Reads the client config with the specified default value.
*
* @param defaultValue
* The default value returned if no value found in directory.
*
* @return The client config value as read from directory.
*/
public T read(T defaultValue)
{
return read(null, null, defaultValue, null, null, null);
}
/**
* Reads the client config with the specified default value.
*
* @param defaultValue
* The default value returned if no value found in directory.
* @param placeholderValue
* The value to replace the placeholder in the directory node name for the config.
*
* @return The client config value as read from directory.
*/
public T read(T defaultValue, String placeholderValue)
{
return read(null, null, defaultValue, null, placeholderValue, null);
}
/**
* Reads the client config with the specified default value.
*
* @param defaultValue
* The default value returned if no value found in directory.
* @param placeholderValue
* The value to replace the placeholder in the directory node name for the config.
* @param ds
* The directory service.
*
* @return The client config value as read from directory.
*/
public T read(T defaultValue, String placeholderValue, DirectoryService ds)
{
return read(null, null, defaultValue, ds, placeholderValue, null);
}
/**
* Reads the client config with the specified default value.
*
* @param defaultValue
* The default value returned if no value found in directory.
* @param webDriverType
* The web driver type.
*
* @return The client config value as read from directory.
*/
public T read(T defaultValue, WebDriverType webDriverType)
{
return read(webDriverType, null, defaultValue, null, null, null);
}
/**
* Reads the client config with the specified default value.
*
* @param defaultValue
* The default value returned if no value found in directory.
* @param webDriverType
* The web driver type.
* @param placeholderValue
* The value to replace the placeholder in the directory node name for the config.
*
* @return The client config value as read from directory.
*/
public T read(T defaultValue, WebDriverType webDriverType, String placeholderValue)
{
return read(webDriverType, null, defaultValue, null, placeholderValue, null);
}
/**
* Reads the client config with the specified default value.
*
* @param defaultValue
* The default value returned if no value found in directory.
* @param scope
* The directory scope.
*
* @return The client config value as read from directory.
*/
public T read(T defaultValue, Utils.DirScope scope)
{
return read(null, scope, defaultValue, null, null, null);
}
/**
* Reads the client config with the specified default value.
*
* @param defaultValue
* The default value returned if no value found in directory.
* @param scope
* The directory scope.
* @param accountIds
* The account ids for the directory lookup.
*
* @return The client config value as read from directory.
*/
public T read(T defaultValue, Utils.DirScope scope, String[] accountIds)
{
return read(null, scope, defaultValue, null, null, accountIds);
}
/**
* Reads the client config with the specified default value.
*
* @param defaultValue
* The default value returned if no value found in directory.
* @param ds
* The directory service.
* @param scope
* The directory scope.
* @param accountIds
* The account ids for the directory lookup.
*
* @return The client config value as read from directory.
*/
public T read(T defaultValue, DirectoryService ds, Utils.DirScope scope, String[] accountIds)
{
return read(null, scope, defaultValue, ds, null, accountIds);
}
/**
* Reads the client config with the specified default value.
*
* @param defaultValue
* The default value returned if no value found in directory.
* @param ds
* The directory service.
*
* @return The client config value as read from directory.
*/
public T read(T defaultValue, DirectoryService ds)
{
return read(null, null, defaultValue, ds, null, null);
}
/**
* Reads client-side the config with the specified default value.
*
* @param directory
* The remote directory API.
* @param search
* Must be one of {@link Directory#ID_ABSOLUTE} (if the given node ID includes all paths
* from the root of the directory tree to the node being read) or {@link Directory#ID_RELATIVE}.
* @param defaultValue
* The default value returned if no value found in directory.
*
* @return The client config value as read with the directory rpc.
*/
public T readInClient(Directory directory, int search, T defaultValue)
{
// add more supported types when needed
if (valueClass == Boolean.class)
{
return (T) directory.getBoolean(search, CLIENT_CONF_CONTAINER + nodeName, (Boolean) defaultValue);
}
return null;
}
/**
* Reads the client config with the specified default value.
*
* @param webDriverType
* The web driver type.
* @param scope
* The directory scope.
* @param defaultValue
* The default value returned if no value found in directory.
* @param ds
* The directory service.
* @param placeholderValue
* The value to replace the placeholder in the directory node name for the config.
* @param accountIds
* The account ids for the directory lookup.
*
* @return The client config value as read from directory.
*/
public T read(WebDriverType webDriverType,
Utils.DirScope scope,
T defaultValue,
DirectoryService ds,
String placeholderValue,
String[] accountIds)
{
String finalNodeName = placeholderValue != null ? String.format(nodeName, placeholderValue) : nodeName;
if (type == Type.CFG_OVERRIDES && !local.get().areCfgOverridesInitialized.get() || type == Type.RUNTIME)
{
if (LOG.isLoggable(Level.INFO))
{
LOG.log(Level.INFO, "ConfigItem '%s' can't be read from directory. Type %s.", finalNodeName, type);
}
return null;
}
if (type == Type.CFG_OVERRIDES && local.get().areCfgOverridesInitialized.get())
{
if (valueClass != String.class)
{
LOG.log(Level.INFO,
"ConfigItem '%s' should be from the String.class to be read from cfgOverrides.",
finalNodeName);
return null;
}
Map<String, String> cfgOverrides = local.get().cfgOverrides;
return cfgOverrides.containsKey(this.optionName) ?
(T) cfgOverrides.get(this.optionName) :
defaultValue;
}
if (scope == null)
{
scope = Utils.DirScope.SERVER;
}
if (webDriverType != null)
{
String driverSpecificContainer = webDriverType.toString().toLowerCase();
// first check the web driver type containers gui/, chui/, embedded/ in webClient
String node = WEB_CLIENT_CONTAINER + driverSpecificContainer + "/" + finalNodeName;
T value = readFullNode(node, scope, null, ds, accountIds);
if (value != null)
{
return value;
}
// second check the web driver type containers gui/, chui/, embedded/ in clientConfig
node = CLIENT_CONF_CONTAINER + driverSpecificContainer + "/" + finalNodeName;
value = readFullNode(node, scope, null, ds, accountIds);
if (value != null)
{
return value;
}
}
if (type == Type.WEB_CLIENT)
{
// then check the webClient container
T value = readFullNode(WEB_CLIENT_CONTAINER + finalNodeName, scope, null, ds, accountIds);
if (value != null)
{
return value;
}
}
finalNodeName = type == Type.SERVER_ACCOUNT ? finalNodeName : CLIENT_CONF_CONTAINER + finalNodeName;
// last check the clientConfig container
return readFullNode(finalNodeName, scope, defaultValue, ds, accountIds);
}
/**
* Reads the client config with the specified default value.
*
* @param fullNode
* The full name of the node.
* @param scope
* The directory scope.
* @param defaultValue
* The default value returned if no value found in directory.
* @param ds
* The directory service.
* @param accountIds
* The account ids for the directory lookup.
*
* @return The client config value as read from directory.
*/
private T readFullNode(String fullNode,
Utils.DirScope scope,
T defaultValue,
DirectoryService ds,
String[] accountIds)
{
if (valueClass == String.class)
{
return (T) Utils.getDirectoryNodeString(ds, fullNode, (String) defaultValue, scope, accountIds);
}
if (valueClass == Integer.class)
{
return (T) Utils.getDirectoryNodeInt(ds, fullNode, (Integer) defaultValue, scope, accountIds);
}
if (valueClass == Boolean.class)
{
return (T) Utils.getDirectoryNodeBoolean(ds, fullNode, (Boolean) defaultValue, scope, accountIds);
}
if (valueClass == List.class)
{
return (T) Utils.getDirectoryNodeStrings(ds, fullNode, scope, accountIds);
}
if (valueClass == Map.class)
{
return (T) Utils.getDirectoryNodeMap(ds, fullNode, null, scope, false, accountIds);
}
return null;
}
}