MultiSessionAppserverDefinition.java
/*
** Module : MultiSessionAppserverDefinition.java
** Abstract : Multi-session appserver definition.
**
** Copyright (c) 2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------Description----------------------------------
** 001 GBB 20250122 Created initial version.
*/
/*
** 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.appserver;
import com.goldencode.p2j.directory.*;
import com.goldencode.p2j.main.*;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.osresource.*;
import java.util.*;
import java.util.concurrent.*;
/** Multi-session appserver definition. */
public class MultiSessionAppserverDefinition
extends AppserverDefinition
{
/** A map of all initialized appserver definitions. */
private static final Map<String, MultiSessionAppserverDefinition> definitions = new ConcurrentHashMap<>();
/** Enabled server-side OS resources for the appserver processes. */
private final Set<OsResourceType> serverSideResources = new HashSet<>();
/** Startup params for agents */
private final String agentStartupParam;
/** Max worker threads per agent */
private final int maxConnectionsPerAgent;
/** Max MSA sessions per agent */
private final int maxMsaSessionsPerAgent;
/** Initial agents */
private final int numInitialAgents;
/** Max agents */
private final int maxAgents;
/** Min agents */
private final int minAgents;
/** Maximum number of agents that may be started at the same time */
private final int agentStartLimit;
/** Wait time before marking an idle agent for termination */
private final int idleAgentTimeout;
/** Wait time before a new agent is considered unresponsive */
private final int agentListenerTimeout;
/**
* In FWD the worker thread idle time before it gets terminated.
* In OE the idle time of the connection between the agent and the session manager.
*/
private final int idleConnectionTimeout;
/**
* Watchdog interval for terminating idle resources (e.g. connection, agent, or client session).
* In milliseconds.
*/
private final int idleResourceTimeout;
/** Initial MSA sessions per agent */
private final int numInitialSessions;
/** Wait time before marking an idle client connection for termination. */
private final int idleSessionTimeout;
/** Wait time to find an existing available connection. NOT USED. */
private final int connectionWaitTimeout;
/**
* Wait time to start a new connection, run the agent and session startup procs and hear back from the agent.
* In milliseconds.
*/
private final int requestWaitTimeout;
/** Max age of the MSA session before being marked for termination. In milliseconds. */
private final int maxMsaSessionAge;
/**
* Raises STOP condition in the MSA session if the request process time exceeds the limit. In seconds.
*/
private final int sessionExecutionTimeLimit;
/** Runs once per agent in the first MSA session */
private final String agentStartupProc;
/** Params for agentStartupProc */
private final String agentStartupProcParam;
/** Runs once per agent on agent termination */
private final String agentShutdownProc;
/** Runs once per MSA session, sets context accessible from only the same MSA session */
private final String sessionStartupProc;
/** Params for sessionStartupProc */
private final String sessionStartupProcParam;
/** Runs once per MSA session on termination */
private final String sessionShutdownProc;
/** Runs on starting execution of a new request from a session-free or unbound session-managed connection. */
private final String sessionActivateProc;
/** Runs on ending execution of a request from a session-free or unbound session-managed connection. */
private final String sessionDeactivateProc;
/** Runs once in the agent when a session-managed connection connects. */
private final String sessionConnectProc;
/** Runs once in the agent when a session-managed connection disconnects */
private final String sessionDisconnProc;
/** Wait timeout for a request on idleAgentTimeout agent shutdown. */
private final int defaultAgentWaitToFinish;
/**
* Wait timeout for a request to complete before forcefully terminating the agent. Triggered by
* idleAgentTimeout agent shutdown.
*/
private final int defaultAgentWaitAfterStop;
/** Emergency shutdown active request wait timeout. */
private final int completeActiveReqTimeout;
/** OS user the JVM runs under. */
private final String systemUser;
/** Client-side parameters. */
private final ClientParameters clientParameters;
/** Enable debugging for multi-session agents. */
private final boolean debugMsa;
/** LOG-MANAGER file name patterned. */
private String agentLogFile;
/** LOG-MANAGER entry types. */
private String agentLogEntryTypes;
/** LOG-MANAGER logging level. */
private Integer agentLoggingLevel;
/** LOG-MANAGER file threshold size. */
private Integer agentLogThreshold;
/** LOG-MANAGER max files count. */
private Integer agentNumLogFiles;
/**
* Package-private constructor. Must be called from threads which have their context authorized to
* access the <code>appservers</code> node for the P2J server.
*
* @param appserverName
* The application's server name.
* @param ds
* Directory service.
* @param appSrvPath
* The appserver node path in directory.
*/
MultiSessionAppserverDefinition(String appserverName, DirectoryService ds, String appSrvPath)
{
super(appserverName, ds, appSrvPath);
this.debugMsa = resolveBoolean(ds, appSrvPath, "debugMsa", false);
this.agentStartupParam = resolveString(ds, appSrvPath, "agentStartupParam", null);
this.maxConnectionsPerAgent = resolveInt(ds, appSrvPath, "maxConnectionsPerAgent", 16);
this.maxMsaSessionsPerAgent = resolveInt(ds, appSrvPath, "maxMsaSessionsPerAgent", 200);
this.numInitialAgents = resolveInt(ds, appSrvPath, "numInitialAgents", 1);
this.maxAgents = resolveInt(ds, appSrvPath, "maxAgents", 10);
this.minAgents = resolveInt(ds, appSrvPath, "minAgents", 0);
this.agentStartLimit = resolveInt(ds, appSrvPath, "agentStartLimit", 1);
this.idleAgentTimeout = resolveInt(ds, appSrvPath, "idleAgentTimeout", 300_000);
this.agentListenerTimeout = resolveInt(ds, appSrvPath, "agentListenerTimeout", 300_000);
this.idleConnectionTimeout = resolveInt(ds, appSrvPath, "idleConnectionTimeout", 300_000);
this.idleResourceTimeout = resolveInt(ds, appSrvPath, "idleResourceTimeout", 1000 * 60 * 30);
this.numInitialSessions = resolveInt(ds, appSrvPath, "numInitialSessions", 5);
this.idleSessionTimeout = resolveInt(ds, appSrvPath, "idleSessionTimeout", 1_800_000);
this.connectionWaitTimeout = resolveInt(ds, appSrvPath, "connectionWaitTimeout", 3000);
this.requestWaitTimeout = resolveInt(ds, appSrvPath, "requestWaitTimeout", 15_000);
this.maxMsaSessionAge = resolveInt(ds, appSrvPath, "maxMsaSessionAge", 0);
this.sessionExecutionTimeLimit = resolveInt(ds, appSrvPath, "sessionExecutionTimeLimit", 0);
this.agentLoggingLevel = resolveInt(ds,
appSrvPath,
"agentLoggingLevel",
LegacyLogOps.DEFAULT_LOGGING_LEVEL);
this.agentLogThreshold = resolveInt(ds,
appSrvPath,
"agentLogThreshold",
LegacyLogOps.DEFAULT_THRESHOLD);
this.agentNumLogFiles = resolveInt(ds,
appSrvPath,
"agentNumLogFiles",
LegacyLogOps.DEFAULT_NUM_FILES);
this.agentLogFile = resolveString(ds,
appSrvPath,
"agentLogFile",
LegacyLogOps.DEFAULT_APPSERVER_LOGFILE_NAME);
this.agentLogEntryTypes = resolveString(ds,
appSrvPath,
"agentLogEntryTypes",
LegacyLogOps.DEFAULT_APPSERVER_TYPES);
this.agentStartupProc = resolveString(ds, appSrvPath, "agentStartupProc", null);
this.agentStartupProcParam = resolveString(ds, appSrvPath, "agentStartupProcParam", null);
this.agentShutdownProc = resolveString(ds, appSrvPath, "agentShutdownProc", null);
this.sessionStartupProc = resolveString(ds, appSrvPath, "sessionStartupProc", null);
this.sessionStartupProcParam = resolveString(ds, appSrvPath, "sessionStartupProcParam", null);
this.sessionShutdownProc = resolveString(ds, appSrvPath, "sessionShutdownProc", null);
this.sessionActivateProc = resolveString(ds, appSrvPath, "sessionActivateProc", null);
this.sessionDeactivateProc = resolveString(ds, appSrvPath, "sessionDeactivateProc", null);
this.sessionConnectProc = resolveString(ds, appSrvPath, "sessionConnectProc", null);
this.sessionDisconnProc = resolveString(ds, appSrvPath, "sessionDisconnProc", null);
this.defaultAgentWaitToFinish = resolveInt(ds, appSrvPath, "defaultAgentWaitToFinish", 30_000);
this.defaultAgentWaitAfterStop = resolveInt(ds, appSrvPath, "defaultAgentWaitAfterStop", 30_000);
this.completeActiveReqTimeout = resolveInt(ds, appSrvPath, "completeActiveReqTimeout", 600_000);
this.systemUser = System.getProperty("user.name");
// load the native library to find the pid and os user.
if (!ClientCore.isNativeLayerInitialized())
{
System.loadLibrary("p2j");
}
this.clientParameters = new ClientParameters();
this.clientParameters.osUserName = systemUser;
this.clientParameters.osDeviceId = ClientCore.getOsDeviceId();
this.clientParameters.chui = true;
this.clientParameters.isHeadless = true;
this.clientParameters.pid = ClientCore.getPid();
this.clientParameters.clientTimezone = ClientCore.getTimezone();
// clientParameters.terminalName used by ConnectTableUpdater.databaseConnected
definitions.put(appserverName, this);
serverSideResources.addAll(getServerSideResForAccount(ds, getProcessName()));
if (serverSideResources.contains(OsResourceType.ALL))
{
serverSideResources.clear();
serverSideResources.add(OsResourceType.ALL);
}
LegacyLogManagerConfigs legacyLogManagerConfigs =
new LegacyLogManagerConfigs(agentLogFile,
String.valueOf(agentLoggingLevel),
agentLogEntryTypes,
agentLogThreshold,
agentNumLogFiles,
clientParameters.pid,
0,
systemUser,
getProcessName(),
true);
legacyLogManagerConfigs.setAppServer(appserverName);
LegacyLogOps.appserverNameLogConfigPairs.putIfAbsent(appserverName, legacyLogManagerConfigs);
}
/**
* Get the settings for the specified appserver.
*
* @param appServer
* The name of the appserver.
*
* @return The instance containing the settings for the specified appserver.
*/
public static MultiSessionAppserverDefinition get(String appServer)
{
return definitions.get(appServer);
}
/**
* Returns an array with all instantiated appserver process definitions.
*
* @return An array with all instantiated appserver process definitions.
*/
public static MultiSessionAppserverDefinition[] getAll()
{
return definitions.values().toArray(new MultiSessionAppserverDefinition[0]);
}
/**
* Returns the number of MSA appservers defined in directory.
*
* @return See above.
*/
public static int count()
{
return definitions.size();
}
/**
* Returns a Map of MSA appserver definitions by their names.
*
* @return See above.
*/
public static Map<String, MultiSessionAppserverDefinition> getDefinitions()
{
return definitions;
}
/**
* Returns <code>true</code> if this definition is for a multi-session agent appserver.
*
* @return See above.
*/
@Override
public boolean isMultiSession()
{
return true;
}
/**
* Getter for {@link #systemUser}.
*
* @return See above.
*/
@Override
public String getSystemUser()
{
// MSA server-side runs under the same user as the server JVM
return systemUser;
}
/**
* Returns an unmodifiable copy of the collection of OS resources configured for the appserver account.
*
* @return See above.
*/
public Collection<OsResourceType> getEnabledServerSideResources()
{
return Collections.unmodifiableCollection(serverSideResources);
}
/**
* Returns <code>true</code> if the OS resources configured for the appserver account contain "all",
* <code>false</code> otherwise.
*
* @return See above.
*/
public boolean isAllServerSide()
{
return serverSideResources.contains(OsResourceType.ALL);
}
/**
* Getter for {@link #agentStartupParam}.
*
* @return See above.
*/
public String getAgentStartupParam()
{
return agentStartupParam;
}
/**
* Getter for {@link #maxConnectionsPerAgent}.
*
* @return See above.
*/
public int getMaxConnectionsPerAgent()
{
return maxConnectionsPerAgent;
}
/**
* Getter for {@link #maxMsaSessionsPerAgent}.
*
* @return See above.
*/
public int getMaxMsaSessionsPerAgent()
{
return maxMsaSessionsPerAgent;
}
/**
* Getter for {@link #numInitialAgents}.
*
* @return See above.
*/
public int getNumInitialAgents()
{
return numInitialAgents;
}
/**
* Getter for {@link #maxAgents}.
*
* @return See above.
*/
public int getMaxAgents()
{
return maxAgents;
}
/**
* Getter for {@link #minAgents}.
*
* @return See above.
*/
public int getMinAgents()
{
return minAgents;
}
/**
* Getter for {@link #agentStartLimit}.
*
* @return See above.
*/
public int getAgentStartLimit()
{
return agentStartLimit;
}
/**
* Getter for {@link #idleAgentTimeout}.
*
* @return See above.
*/
public int getIdleAgentTimeout()
{
return idleAgentTimeout;
}
/**
* Getter for {@link #agentListenerTimeout}.
*
* @return See above.
*/
public int getAgentListenerTimeout()
{
return agentListenerTimeout;
}
/**
* Getter for {@link #idleConnectionTimeout}.
*
* @return See above.
*/
public int getIdleConnectionTimeout()
{
return idleConnectionTimeout;
}
/**
* Getter for {@link #idleResourceTimeout}.
*
* @return See above.
*/
public int getIdleResourceTimeout()
{
return idleResourceTimeout;
}
/**
* Getter for {@link #numInitialSessions}.
*
* @return See above.
*/
public int getNumInitialSessions()
{
return numInitialSessions;
}
/**
* Getter for {@link #idleSessionTimeout}.
*
* @return See above.
*/
public int getIdleSessionTimeout()
{
return idleSessionTimeout;
}
/**
* Getter for {@link #connectionWaitTimeout}.
*
* @return See above.
*/
public int getConnectionWaitTimeout()
{
return connectionWaitTimeout;
}
/**
* Getter for {@link #requestWaitTimeout}.
*
* @return See above.
*/
public int getRequestWaitTimeout()
{
return requestWaitTimeout;
}
/**
* Getter for {@link #maxMsaSessionAge}.
*
* @return See above.
*/
public int getMaxMsaSessionAge()
{
return maxMsaSessionAge;
}
/**
* Getter for {@link #sessionExecutionTimeLimit}.
*
* @return See above.
*/
public int getSessionExecutionTimeLimit()
{
return sessionExecutionTimeLimit;
}
/**
* Getter for {@link #agentStartupProc}.
*
* @return See above.
*/
public String getAgentStartupProc()
{
return agentStartupProc;
}
/**
* Getter for {@link #agentStartupProcParam}.
*
* @return See above.
*/
public String getAgentStartupProcParam()
{
return agentStartupProcParam;
}
/**
* Getter for {@link #agentShutdownProc}.
*
* @return See above.
*/
public String getAgentShutdownProc()
{
return agentShutdownProc;
}
/**
* Getter for {@link #sessionStartupProc}.
*
* @return See above.
*/
public String getSessionStartupProc()
{
return sessionStartupProc;
}
/**
* Getter for {@link #sessionStartupProcParam}.
*
* @return See above.
*/
public String getSessionStartupProcParam()
{
return sessionStartupProcParam;
}
/**
* Getter for {@link #sessionShutdownProc}.
*
* @return See above.
*/
public String getSessionShutdownProc()
{
return sessionShutdownProc;
}
/**
* Getter for {@link #sessionActivateProc}.
*
* @return See above.
*/
public String getSessionActivateProc()
{
return sessionActivateProc;
}
/**
* Getter for {@link #sessionDeactivateProc}.
*
* @return See above.
*/
public String getSessionDeactivateProc()
{
return sessionDeactivateProc;
}
/**
* Getter for {@link #sessionConnectProc}.
*
* @return See above.
*/
public String getSessionConnectProc()
{
return sessionConnectProc;
}
/**
* Getter for {@link #sessionDisconnProc}.
*
* @return See above.
*/
public String getSessionDisconnProc()
{
return sessionDisconnProc;
}
/**
* Getter for {@link #defaultAgentWaitToFinish}.
*
* @return See above.
*/
public int getDefaultAgentWaitToFinish()
{
return defaultAgentWaitToFinish;
}
/**
* Getter for {@link #defaultAgentWaitAfterStop}.
*
* @return See above.
*/
public int getDefaultAgentWaitAfterStop()
{
return defaultAgentWaitAfterStop;
}
/**
* Getter for {@link #completeActiveReqTimeout}.
*
* @return See above.
*/
public int getCompleteActiveReqTimeout()
{
return completeActiveReqTimeout;
}
/**
* Getter for {@link #clientParameters}.
*
* @return See above.
*/
public ClientParameters getClientParameters()
{
return clientParameters;
}
/**
* Getter for {@link #agentLogFile}.
*
* @return See above.
*/
public String getLogFilename()
{
return agentLogFile;
}
/**
* Getter for {@link #agentLoggingLevel}.
*
* @return See above.
*/
public Integer getLoggingLevel()
{
return agentLoggingLevel;
}
/**
* Getter for {@link #agentLogThreshold}.
*
* @return See above.
*/
public Integer getLogFileThresholdSize()
{
return agentLogThreshold;
}
/**
* Getter for {@link #agentNumLogFiles}.
*
* @return See above.
*/
public Integer getLogFilesMaxNumber()
{
return agentNumLogFiles;
}
/**
* Getter for {@link #agentLogEntryTypes}.
*
* @return See above.
*/
public String geLogEntryTypes()
{
return agentLogEntryTypes;
}
/**
* Getter for {@link #debugMsa}.
*
* @return See above.
*/
public boolean isDebugMsa()
{
return debugMsa;
}
/**
* Reads the OS resources configured for the appserver account and returns a collection of the types.
*
* @param ds
* Directory service.
* @param account
* The FWD account for the appserver.
*
* @return A collection of the OS resource types, configured for the appserver account.
*/
private Collection<OsResourceType> getServerSideResForAccount(DirectoryService ds, String account)
{
Set<OsResourceType> serverSideResources = new HashSet<>();
String res = Utils.getDirectoryNodeString(ds,
"server-side-resources",
"",
Utils.DirScope.BOTH,
new String[]{account});
for (String resName : res.split(","))
{
resName = resName.trim();
if (resName.isEmpty())
{
continue;
}
OsResourceType resType = OsResourceType.find(resName);
if (resType == null)
{
continue;
}
serverSideResources.add(resType);
}
return serverSideResources;
}
/**
* Returns the int value of the node.
*
* @param ds
* Directory service.
* @param appSrvPath
* The appserver node path in directory.
* @param nodeId
* The node id to read.
* @param defaultValue
* The default value.
*
* @return The int value of the node.
*/
private int resolveInt(DirectoryService ds, String appSrvPath, String nodeId, int defaultValue)
{
Integer value = ds.getNodeInteger(appSrvPath, nodeId);
return value == null ? defaultValue : value;
}
/**
* Returns the String value of the node.
*
* @param ds
* Directory service.
* @param appSrvPath
* The appserver node path in directory.
* @param nodeId
* The node id to read.
* @param defaultValue
* The default value.
*
* @return The String value of the node.
*/
private String resolveString(DirectoryService ds, String appSrvPath, String nodeId, String defaultValue)
{
String value = ds.getNodeString(appSrvPath, nodeId);
return value == null || value.isEmpty() ? defaultValue : value.trim();
}
/**
* Returns the Boolean value of the node.
*
* @param ds
* Directory service.
* @param appSrvPath
* The appserver node path in directory.
* @param nodeId
* The node id to read.
* @param defaultValue
* The default value.
*
* @return The String value of the node.
*/
private Boolean resolveBoolean(DirectoryService ds, String appSrvPath, String nodeId, Boolean defaultValue)
{
Boolean value = ds.getNodeBoolean(appSrvPath, nodeId);
return value == null ? defaultValue : value;
}
}