Appserver Support¶
- Appserver Support
Configuration¶
Appserver configuration is done in FWD's directory. This includes the appserver details, the FWD process account under which the appserver Agent is executing, and more. This section will describe what configuration is required to setup an appserver in FWD's directory.
In the examples in this section:- the appserver is assumed to be named
app_server
- the FWD process account is assumed to be named
appserver_process
Add the Process Account¶
Follow Batch Processes to add a FWD process account in directory.xml
. The final definition should look like:
<node class="process" name="appserver_process"> <node-attribute name="appserver" value="app_server"/> <node-attribute name="enabled" value="TRUE"/> <node-attribute name="description" value="Process for appserver."/> <node-attribute name="alias" value="appserver_process"/> <node-attribute name="master" value="FALSE"/> <node-attribute name="server" value="FALSE"/> </node>
The certificate must be installed using Cryptography Setup Helper.
Configure the Scheduler¶
Follow Scheduler to schedule the automatic launch of the app_server
at startup. The final configuration should look like this:
<node class="container" name="scheduler"> <node class="job" name="start_appserver"> <node-attribute name="type" value="process"/> <node-attribute name="target" value="appserver_process"/> <node-attribute name="enabled" value="TRUE"/> <node-attribute name="mode" value="now"/> </node> </node>
Configure the Spawner¶
Follow Spawner setup to configure the clientConfig
node for the FWD process associated with the appserver.
Configure the Appserver¶
Follow Configuring a Legacy Appserver to configure the appserver details in FWD. This configuration is for launching the appserver Agents, and not for appserver connections. The result should look like this:
<node class="container" name="appservers"> <node class="appserver" name="app_server"> <node-attribute name="operating_mode" value="state-free"/> <node-attribute name="request_timeout" value="0"/> <node-attribute name="auto_trim_timeout" value="30"/> <node-attribute name="propath" value=".:appsrv/api:"/> <node-attribute name="initial_agents" value="1"/> <node-attribute name="min_agents" value="1"/> <node-attribute name="max_agents" value="1"/> <node-attribute name="activate" value=""/> <node-attribute name="deactivate" value=""/> <node-attribute name="connect" value=""/> <node-attribute name="disconnect" value=""/> <node-attribute name="startup" value=""/> <node-attribute name="shutdown" value=""/> <node-attribute name="startup_parameter" value=""/> </node> </node>
Add as many appservers
configurations as required, with their configuration usually being extracted from the OpenEdge ubroker.properties
files.
Configure the Appserver for Remote Connections¶
If the appserver is accessed from 4GL code (remote or local for the FWD server), app_services
, follow Configuring an Appserver Client to specify the details. The result may look like this:
<node class="container" name="app_services"> <node class="container" name="app_server"> <node class="integer" name="p2j_port"> <node-attribute name="value" value="3333"/> </node> <node class="string" name="p2j_host"> <node-attribute name="value" value="localhost"/> </node> <node class="string" name="p2j_account"> <node-attribute name="value" value="appserver_agent"/> </node> <node class="boolean" name="sessionFree"> <node-attribute name="value" value="TRUE"/> </node> <node class="strings" name="aliases"/> <node class="strings" name="hosts"> <node-attribute name="values" value="localhost"/> </node> <node class="integers" name="ports"> <node-attribute name="values" value="21200"/> <node-attribute name="values" value="21300"/> <node-attribute name="values" value="21500"/> </node> <node class="strings" name="services"> <node-attribute name="values" value="fwd1"/> <node-attribute name="values" value="21200"/> <node-attribute name="values" value="21300"/> <node-attribute name="values" value="21500"/> </node> </node> </node>
p2j_account
needs to be specified only when connecting to a remote appserver; for only local 4GL SERVER:CONNECT
method usage, this can be skipped.
Starting the Appserver¶
On FWD startup, all appserver configuration from the appservers
node is read into AppServerDefinition
. This will be used to determine the appserver details, when an Agent is started.
The appserver Agents are started automatically when the FWD server starts, assuming the Scheduler is configured for this. When the FWD client (process in this case) starts, StandardServer.standardEntry
will check if AppServerManager.startAppServer()
can handle an appserver Agent. A number of initial_agents
appserver Agents (configured for an appserver) will be started automatically at FWD server startup.
If all agents started automatically at FWD server startup are busy, FWD will automatically launch an Agent for the target appserver, until max_agents
is reached, when a appserver call is performed.
Appserver agents can also be manually started via ./server.sh -b <process>
(assuming max_agents
is not reached).
Agent trimming is done automatically, via the FWD's Scheduler support, which will schedule a AppServerLauncher.trimAgents
task for each appserver, called every auto_trim_timeout
seconds. When called, Agents will be terminated until min_agents
is reached.
AgentPool.newPool(appServer)
. This creates either a AgentPool$BoundPool
or AgentPool$UnboundPool
, depending on the appserver's type:
- a
BoundPool
instance forState-aware
andState-reset
appservers - a
UnboundPool
instance forStateless
andState-free
appservers
Conversation
, Reader
and Writer
thread, as for any other FWD client. The Agent's Conversation
thread is not responsible for accepting directly an appserver request - the Conversation
thread just waits for tasks to be posted. Instead, a remote request will be executed on the Dispatcher
FWD server threads, which will call the proper AppServerManager
API, to:
- identify an available Agent and retrieve it from the appserver pool (it will wait for a number of
request_timeout
seconds until an Agent is available). - post the task to the Agent's
Conversation
thread. - wait for the task to complete.
- return the result back to the caller.
For this reason, if there are remote appserver requests (this includes OpenClient calls when the client is remote and not running in the same JVM as the FWD server), it is important to configure at the FWD Server a number Dispatcher
threads which can handle the maximum number of simultaneous remote connections.
When the request is originating from the same JVM thread as the FWD server, the Dispatcher
threads will not be used, and the caller will be linked with the AppServerManager
directly, via a local proxy.
Connecting to AppServer¶
Appserver connections can be established via the SERVER:CONNECT
method or via an FWD Open Client connection. The appserver connection approach differs in each case, FWD providing support of the 4GL connection options only for the SERVER:CONNECT
method.
Supported SERVER:CONNECT
method options¶
FWD supports both PASOE and Classic appserver connection options:
- for PASOE, the following options are supported:
-pf filename
-sessionModel [ Session-managed | Session-free ]
-URL Web-or-AppServer-path
- for Classic Appserver, these options are supported:
-AppService application-service
-H [ host_name | IP-address ]
-S [ service-name | port-number ]
-DirectConnect
- if set, then-H
and-S
specify the appserver, otherwise the nameserver target.-URL Web-or-AppServer-path
- when this is set, then host and port are extracted,-directConnect
is assumed, and-S
is assumed the URI path-sessionModel [ Session-managed | Session-free ]
The following appserver connection options are not supported:
-ssl -nosessionreuse -nohostverify -connectionLifetime -initialConnections -maxConnections -nsClientMaxPort -nsClientMinPort -nsClientPicklistExpiration -nsClientPicklistSize -nsClientPortRetry -nsClientDelay -AppServerKeepaliveThe target appserver is being resolved using the Configuring an Appserver Client options, following these rules:
- if
-sessionModel
is set, then:- when
Session-free
,app_services/<appserver>/sessionFree
must be set totrue
. - when
Session-managed
,app_services/<appserver>/sessionFree
must be set tofalse
.
- when
- either
-directConnect
is set or, otherwise,app_services/<appserver>/aliases
must contain the-AppService
name. app_services/<appserver>/hosts
contains the host from-H
options or from the specified-URL
; if neither is set, then it must containlocalhost
.- if
-S port
or-URL
is set, thenapp_services/<appserver>/ports
must have this port - if
-S service
is set, thenapp_services/<appserver>/services
must have the service name - if neither
-S
or-URL
are set, the port is assumed to be5162
, andapp_services/<appserver>/ports
must have this port - if neither
-H
or-URL
is set, the host is assumed to belocalhost
, andapp_services/<appserver>/hosts
must have this host - if
-AppService
is not set and not in direct connnect mode, then-AppService
is assumed to be the default service configured for that nameserver host (as the specified or implicit-H
is assumed to target a nameserver).
Connecting from OpenClient¶
Connecting from a remote client is done by using the FWD OpenClient APIs. This requires first to configure the connection to the remote server, and after that call LegacyJavaAppserverClient.connectRemote
to establish the appserver connection. Details are specified at Proxygen_REST_SOAP_and_WebHandler.
Runtime Details¶
A named appserver in FWD is a pool of agents which all share the same configuration (of which the most important are the propath and session mode) and which can be targeted via options specified in the connect parameters for a 4GL <server_handle>:CONNECT()
method call, or the equivalent connect API for an Open Client. The agents in a pool (for an appserver) all share the same appservers/<appserver>
configuration.
-sessionModel
is not explicitly used to resolve the target appserver - all the other connection options (like -AppService
or -URL
) are used to find the appserver. Once this is found, when -sessionModel
is specified, it means that:
- if
Session-Free
, the target appserver must be State-free. - if
Session-Managed
, the target appserver must not be State-free.
- if the appserver is running in State-reset or State-aware mode, the Agent used to establish the connection becomes bound to the connection.
- if the appserver is running in Stateless mode, an Agent is bound to that connection only when a program is ran persistent.
- if the appserver is running in State-free mode, an Agent is not bound to the connection when a program is ran persistent, but any invocations targeting a persistent program will always route the request to the Agent which instantiated that persistent program.
Once the Agent is bound to the connection, it will not be released back to the agent pool, and is unavailable for work from other connections; only when it becomes unbound will be added back to the pool.
For an appserver, the work can be posted by the following callers:
Caller Type | Description | Proxy | Proxy Setup Code | Proxy Tear-Down Code | RemoteObject Interface Used |
---|---|---|---|---|---|
local 4GL code | call to appserver from converted code in the same FWD server | local | AppServerHelper.connectLegacyMode |
lifetime of FWD server | AppServerEntry |
remote 4GL code | call to appserver from converted code in a different FWD server, this will occur over a virtual session | RemoteObject | AppServerHelper.connectLegacyMode |
AppServerHelper.disconnect |
AppServerEntry |
local Java Open Client replacement | call to appserver from hand coded Java in the same FWD server | local | AppServerHelper.connect(BootstrapConfig config, String account, String appServerName, boolean sessionFree) |
Lifetime of FWD server. | AppServerEntry |
remote Java Open Client replacement | call to appserver from hand coded Java in the a separate JVM, the call will come from a direct session through the dispatcher | RemoteObject | AppServerHelper.connect(BootstrapConfig config, String account, String appServerName, boolean sessionFree) |
LegacyJavaAppserverClient.disconnect |
AppServerEntry |
legacy REST | internal usage of appserver agents as a worker for processing requests from legacy REST services | local | AppServerHelper.connect(BootstrapConfig config, String account, String appServerName, boolean sessionFree) |
Lifetime of FWD server. | AppServerEntry |
legacy SOAP | internal usage of appserver agents as a worker for processing requests from legacy SOAP services | local | AppServerHelper.connect(BootstrapConfig config, String account, String appServerName, boolean sessionFree) |
Lifetime of FWD server. | AppServerEntry |
legacy WebHandler | internal usage of appserver agents as a worker for processing requests from legacy WebHandler services | local | AppServerHelper.connect(BootstrapConfig config, String account, String appServerName, boolean sessionFree) |
Lifetime of FWD server. | AppServerEntry |
For legacy REST, SOAP or web handler support, FWD uses an embedded web server (Jetty) to handle the HTTP requests. But, the request to the appserver is not being processed on the thread handling the web request - instead, a pool of workers (a distinct pool for each REST, SOAP or web handler support) is used. This worker pool will use a queue on which a task (as calculated on the web thread for that service request) will be posted.
The worker pool was added because the number of active web threads trying to perform i.e. REST requests can be more than the available Agents on the target appserver - if each one of these web threads would try to post a task to the appserver at the same time, the Agent pool would be exhausted immediately, and many request would fail with 'no server available' errors. Having this intermediate pool allows to queue the requests, with each Jetty thread being able to post the work to the worker thread with the fewest tasks on its queue - it will just have to wait for the worker thread to finish all the tasks posted before.
Local 4GL Code¶
Remote 4GL Code¶
Local Open Java Client Replacement¶
Remote Open Java Client Replacement¶
Legacy REST¶
Legacy SOAP¶
Legacy WebHandler¶
Debugging Details¶
All appserver invocations are exported via AppServerEntry
proxy (at the client side), and implemented in AppServerManager
, on FWD server-side.
AppServerManager
will always acquire an Agent (the connection or procedure bound Agent, or one from the pool), and post the work to it. At the Agent
FWD implementation, there are various APIs related to invoking an external program (persistent or not), an internal procedure, etc.
Conversation
thread. The simplest way is to find a common API executed for all calls, and place a breakpoint in it - this is Agent.preProcessArguments
, which converts the arguments to be compatible for being passed to the ControlFlowOps
API call. Placing a breakpoint in this method allows you to exit just before the ControlFlowOps
API call for the target. You can step through the code until this ControlFlowOps
API is reached, and from that:
- if you want to debug the
ControlFlowOps
, step further into through these APIs - otherwise, place a breakpoint in
Block.body()
method, and 'step over' theControlFlowOps
API - this will execute the code until the targeted converted code will be reached - you can continuing stepping into thebodyMethod.body();
call, and you will end up directly in the converted code.
Agent.preProcessArguments
(which executes before ControlFlowOps
) has a Agent.postProcessArguments
paired method which always executes sometime after the ControlFlowOps
API call finishes. Placing a breakpoint in postProcessArguments
allows you to inspect the arguments as they are returned by the API call.
© 2004-2022 Golden Code Development Corporation. ALL RIGHTS RESERVED.