Proxygen, REST, SOAP and WebHandler¶
OpenClient Proxy Clients¶
Input Data¶
For generating the OpenClient proxy programs, FWD uses as input one or more .xpxg
files, generated via the legacy proxygen
tool, for Java or .NET Open Client.
These .xpxg
files will reference 4GL programs, set to be ran non-persistent, persistent, single-run or singleton. These programs can be configured in one or more AppObject (a .xpxg
file) or SubAppObject - a program can not be of the same AppObject or SubAppObject, part more than once.
Configuration:¶
In FWD, the configuration is done viap2j.cfg.xml
parameters:
- specify a single
.xpxg
file or a folder where the.xpxg
files reside, via theproxy-programs-cfg
parameter. If a folder is specified, this will be read recursively. - specify the folder where the Java source code for the generated OpenClient proxies is emitted, via
proxy-output-root
parameter. - specify if the generated Java code will use legacy names (for external program, internal procedure, function, dataset, temp-tables, parameters), or the FWD naming will be used. This is done via the
proxy-legacy-names
, which is optional, and defaults tofalse
(the FWD naming convention will be used).
The Java source code will be emitted in the Java package as specified via the PGGenInfo/Package
entry in the AppObject
. If is missing, the code will be emitted in the proxy
sub-package of the configured pkgroot
parameter in p2j.cfg.xml
.
A sample of these parameters is:
<parameter name="proxy-programs-cfg" value="abl/proxy"/> <!-- folder where the .xpxg exist --> <parameter name="proxy-legacy-names" value="true"/> <!-- uses the legacy names for temp-tables, programs, internal procedures/functions --> <parameter name="proxy-output-root" value="src" /> <!-- folder where to write the proxy programs -->
Conversion Process¶
FWD conversion is built to process 4GL programs or classes. As a 4GL program can be part of multiple (Sub)AppObject configurations, the processing is done in 'batches': each batch will process programs for only one (Sub)AppObject configuration, which will incrementally add its Java code to the JAST for that (Sub)AppObject.
This allows the same program, when is part of multiple (Sub)AppObject, to be emitted in each corresponding Java class for that (Sub)AppObject.
To generate the Java code for all 4GL proxy programs, it can be done:- using a full conversion
- using the
GenerateLegacyProxyOpenClient
- this should be executed in its own conversion project, as it will invalidate any existing conversion in the project folder.
- use incremental conversion to convert the 4GL program
- use
GenerateLegacyProxyOpenClient
tool to generate all the proxy programs, in a separate project folder
Both folder projects are assumed to contain the same 4GL sources.
For conversion, FWD does this in two phases.- first phase is to load the
.xpxg
files and load each (Sub)AppObject intoLegacyProxyConfig
instances, with all the information needed about the 4GL programs exported as OpenClient proxies (each 4GL program referenced in a (Sub)AppObject will have aLegacyProxyClientConfig
instance, where all its details are kept). This loading is done viaServiceSupport.initializeProxyConfigurations
API, and uses as input the.xpxg
files specified via theproxy-programs-cfg
configuration parameter. The conversion requires only to annotate (viaannotations/proxy_programs.xml
) the ASTs (the external program, temp-tables, datasets, procedures, functions, etc), with a specialproxy-client-program
annotation, which will be later used to identify these structures, when the actual Java code for the OpenClient is generated. - the second phase is the actual generation of the Java OpenClient proxy code, where the responsible TRPL rule-set is
convert/proxy_programs.xml
.
Both the above rule-sets rely on ServiceSupport.isLegacyProxyClient
to identify if a file is exposed as a OpenClient proxy.
When GenerateLegacyProxyOpenClient
tool is used, an additional TRPL rule-set is ran before the two TRPL rule-sets above, annotations/legacy_services.xml
- this includes any TRPL rule-sets mandatory for the OpenClient proxy generation (like processing for procedures, functions, class methods, temp-tables, etc)
Proxy Program Structure¶
A Java class will be generated for each (Sub)AppObject. The Java class name will be either the name configured atAppObject/Name
or SubAppObject/Name
. Each Java class will contain:
- methods to run the external proxy program, with its parameters; the naming will be:
- for a persistent, singleton or single-run procedure, the program name, using the
new_
prefix. - for a non-persistent procedure, the program name.
- for a persistent, singleton or single-run procedure, the program name, using the
- methods to create temp-table or dataset
DataGraph
instances, where required by the external program parameters.
Proc Object
concept in OpenClient), which will contain:
- methods for each internal procedure or function
- methods to create temp-table or dataset
DataGraph
instances, where required by the internal procedure or function.
Each of the generated Java class (for (Sub)AppObject or for an external program ran persistent) will inherit the LegacyJavaAppserverClientProxy
class.
- converted using the FWD converter (they will match the names in the associated 4GL program), when
proxy-legacy-names
parameter is missing or set tofalse
incfg/p2j.cfg.xml
. - using the exact legacy name, for the (Sub)AppObject, external program, internal procedures, dataset, temp-tables, etc, when
proxy-legacy-names
flag is set totrue
incfg/p2j.cfg.xml
. TODO: these names are not sanitized to be compatible as Java names, so if the legacy names contains hyphens or other illegal characters in Java names, the code will not compile. This will be fixed #6644.
Runtime Invocation¶
The execution of a Java Open Client proxy (be it a standalone external program invocation, a persistent/singleton/single-run invocation or an internal procedure/function call) starts with establishing a connection to the FWD server where the proxied 4GL programs are running; this is done using theLegacyJavaAppserverClient.connectRemote
API call, with the following parameters:
BootstrapConfig cfg
, with the settings to connect to the remote FWD server.String appserver
, the target appserver serving the 4GL proxied programs.boolean sessionFree
, flag indicating if the target appserver runs inSession-free
mode.
An example of such configuration is:
BootstrapConfig cfg cfg = new BootstrapConfig(); cfg.setServer(false); cfg.setConfigItem("net", "connection", "secure", "false"); cfg.setConfigItem("net", "server", "host", "localhost"); cfg.setConfigItem("net", "server", "insecure_port", "3433"); LegacyJavaAppserverClient fwd = LegacyJavaAppserverClient.connectRemote(cfg, "app_server", true);
The LegacyJavaAppserverClient
instance will have a AppServerHelper helper
instance, which is connected to the remote FWD server.
LegacyJavaAppserverClient
instance, create a new instance for the (Sub)AppObject class associated with the desired 4GL proxied program, by passing the LegacyJavaAppserverClient
instance to its constructor. All calls will be routed via LegacyJavaAppserverClient
APIs to the target FWD server, via one of runFunction
, runProcedure
, runSingleRunProcedure
, runSingletonProcedure
, runPersistentProcedure
APIs. The call flow is:
- the generated Java method for required target calls one of the
run
APIs in theLegacyJavaAppserverClient
instance. LegacyJavaAppserverClient
delegates the call to aAppServerHelper.invoke...
method.- arguments are preprocessed via
AppServerHelper.preProcessParameters
AppServerHelper.invoke...
delegates the call to itsAppServerEntry
instance, which is a network proxy connected to the FWD server - on the FWD server side, there is an equivalent static Java method in theAppServerManager
class.
AppServerHelper.checkResults
will raise any exception as received from the remote side, and will post-process any OUTPUT/INPUT-OUTPUT arguments viapostProcessResult
method.
- arguments are preprocessed via
Dispatcher
threads, which will:
- execute the proper
AppServerManager.invoke...
method. This will determine the proper FWD Agent to handle this request, post the work to the Agent (via one of itsinvoke...
methods), and wait for a response - the Agent's
invoke...
method will rely on one of theControlFlowOps
methods to execute the request - the call is executed as any other external program/internal procedure or function invocation, from the converted code. - The response is returned back to the
AppServerHelper
caller, via theAppServerManager.invoke
method.
REST services¶
Input Data¶
Rest services are configured in OpenEdge via .paar files. This is a zip archive, which contains several files:spring.xml
, from where FWD extracts only the service address. This address is a path, which will be added to the basepath configured in FWD for the REST services.resourceModel.xml
, which defines all REST operations, with their path and verb.mapping.xml
, used to extract the REST parameters (either at path, JSON, cookie, etc) for each REST operation.<serviceName>.restoe
, used to map the 4GL programs with the REST operations
FWD parses these files and matches a REST operation with a 4GL program (or its internal procedures) on the PROPATH. To be considered for a match, the 4GL program or its internal procedures must have the 4GL openapi.openedge.export FILE(type="REST", ...
annotation.
Configuration¶
In FWD, the rest-cfg
configuration parameter in p2j.cfg.xml
is used to specify either a single .paar
file or a folder with multiple .paar
files (which will be read recursively).
A sample of this parameters is:
<parameter name="rest-cfg" value="cfg/goldencodeService.paar" />
Conversion Process¶
When converting a 4GL application configured with REST services, FWD will identify all 4GL programs/classes (and its internal entries) exported as REST services. In each case, it will determine the REST configuration for it, and will enhance the openapi.openedge.export
4GL annotation directly at the program's AST, with the REST configuration.
The .paar
input files are processed via ServiceSupport.resolveRestConfiguration
, which creates data structures identifying the REST service (this includes the REST service name, path, verb, method, parameters). 4GL programs and its internal entries will map to a target REST service using its program name, and, in case of internal entries, the program name suffixed with the internal entry name, using ..
as separator.
During conversion, these openapi.openedge.export
annotations will be emitted as LegacyService
Java annotations. During development process, REST services can be defined directly in the 4GL source code (FWD-only compatible), via annotations like these:
@LegacyService(type = "REST", name = "pipe_integer", path = "/pipe/integer/~{value~}", address = "/goldencodeService", verb = "GET", produces = "application/json", consumes = "application/json", parameters = [ @LegacyServiceParameter(ordinal = 1, name = "inputValue", type = "integer", source = "$~{rest.pathparam['/pipe/integer/~{value~};value']~}", input = true), @LegacyServiceParameter(ordinal = 2, name = "outputValue", type = "integer", target = "$~{json.object['response'].integervalue['outputValue']~}", output = true) ]).
This allows moving away from the
.paar
configuration and having all necessary information directly in the 4GL code.
Any 4GL program which itself or one of its internal procedures/functions exported as a REST service will have added in its name_map.xml
definition the with-services="true"
flag, at the class-mapping
node associated with the 4GL program.
The conversion process gathers data about the 4GL programs/internal entries exposed as REST services during annotations phase, via annotations/legacy_services.rules
- this will process the openapi.openedge.export
annotation, to be converted later as LegacyService
Java annotation, and also expand it with LegacyServiceParameter
annotation and other information. FWD will automatically emit these 4GL-like annotations as Java annotations.
REST services are generated either in full or incremental conversion, with no special management required. For incremental conversion, if a 4GL program configured for REST services is included, its LegacyService
annotation will be automatically generated, the same way as when a full conversion is ran.
If any of the .paar
files are changed, a full conversion must be executed.
annotations/legacy_services.rules
, which uses:
ServiceSupport.isRestService
to determine if a 4GL program or its internal entry map to a REST service- if the top-level block is marked with the 4GL
openapi.openedge.export
annotation, this will be expended to emit asLegacyService
Java annotation, including any requiredLegacyServiceParameter
annotations (for the parameters). Helper APIs are:ServiceSupport.resolveRestAnnotation
to resolve theLegacyService
annotation instance, which will be injected in the ASTServiceSupport.getParameters
, to resolve theLegacyServiceParameter
list, which will be injected in the AST for theLegacyService.parameters
annotation.
Generated Annotations¶
The REST conversion result areLegacyService
annotations emitted in the Java code for the external program and/or its internal entries. An emitted annotation looks like this, for an external program:@LegacyService(type = "REST", executionMode = "external", name = "pipe_integer", path = "/pipe/integer/{value}", address = "/goldencodeService", verb = "GET", produces = "application/json", consumes = "application/json", parameters = { @LegacyServiceParameter(ordinal = 1, name = "inputValue", type = "integer", source = "${rest.pathparam['/pipe/integer/{value};value']}", input = true), @LegacyServiceParameter(ordinal = 2, name = "outputValue", type = "integer", target = "${json.object['response'].integervalue['outputValue']}", output = true) })
where:
type
is always set to"REST"
executionMode
can be"external"
,"singleton"
,"single-run"
or"persistent"
.name
is the REST service operation name.address
is the REST service address, relative tobasepath
configured for the FWD server's REST handler.path
is the REST service path, relative to theaddress
verb
is the HTTP method (GET, POST, DELETE, etc).consumes
andproduces
is the Content-type for the request and response.parameters
is a list ofLegacyServiceParameter
annotation, a one-to-one mapping of the REST parameters with the 4GL programs or internal entry's signature. Each annotation must provide:- parameter's
ordinal
(in the list of the target's 4GL parameters).ordinal = 0
maps a REST parameter with a function's return value. - parameter's
type
(a 4GL data type). - parameter's mode, via
input
andoutput
flags - this allows mapping a parameter asINPUT
,OUTPUT
orINPUT-OUTPUT
. returnValue = true
, only ifordinal = 0
, to specify a function's return type.- parameter's
source
, mandatory if if the parameter hasinput = true
. - parameter's
target
, mandatory if if the parameter hasoutput = true
orreturnValue = true
.
- parameter's
FWD will automatically extract the parameters from the REST request, populate the arguments using FWD compatible data-types (this includes DATASET, TABLE, etc), and also automatically populate the response by serializing the OUTPUT or RETURN parameters to their corresponding target
.
Runtime Invocation¶
At FWD server startup, it will go through all programs configured in name_map.xml
having with-services="true"
attribute, and will check their LegacyService
annotation - if type = "REST"
, it will automatically configure the REST service and map it to the program or internal entry. Context handlers will be created for each defined address
, and a handler for a certain address will process all requests for the REST services configured for it. All context handlers will have as handler the singleton RestHandler
instance.
The last step for initializing the FWD REST service support is to launch a dedicated pool of LegacyServiceWorker
worker threads (via AppServerConnectionPool
), which only handle REST requests: each worker thread will be connected to the appserver (running in the same FWD server) responsible for performing the actual execution of the REST call's target. Each LegacyServiceWorker
worker will have a AppServerHelper
instance, connected via a local proxy to the target appserver (running in the same JVM).
RestHandler.handle
API is responsible for interpreting it; if it finds a RestService
mapping for that target and verb combination, it will post a task to a worker, via a RestHandler.invoke
job, and wait for its completion. The worker will:
- parse the arguments from the REST request and create compatible instances with the target parameters (at the 4GL program or internal entry).
- use the
RestService
instance to determine the properAppServerHelper
API to perform the remote call (i.e. invoke a 4GL program, an internal entry, etc), so it can delegate the call to aAppServerHelper.invoke...
method, where:- arguments are preprocessed via
AppServerHelper.preProcessParameters
AppServerHelper.invoke...
delegates the call to itsAppServerEntry
instance, which is a local proxy for theAppServerManager
static methods.
AppServerHelper.checkResults
will raise any exception as received from the remote side, and will post-process any OUTPUT/INPUT-OUTPUT arguments viapostProcessResult
method.
- arguments are preprocessed via
- the worker waits for the result from the
AppServerHelper.invoke
call - once the result is received, the worker will serialize the response to the proper format/target (response body, cookie, header, etc).
When control is received back by the web thread handling the REST request, the worker thread will have finished the request invocation and processing of the HTTP response.
On server side, the remote request fromRestHandler
is received by one of the Dispatcher
threads, which will:
- execute the proper
AppServerManager.invoke...
method. This will determine the proper FWD Agent to handle this request, post the work to the Agent (via one of itsinvoke...
methods), and wait for a response - the Agent's
invoke...
method will rely on one of theControlFlowOps
methods to execute the request - the call is executed as any other external program/internal procedure or function invocation, from the converted code. - The response is returned back to the
AppServerHelper
caller, via theAppServerManager.invoke
method.
SOAP Services¶
Input Data¶
For SOAP services, FWD uses as input one or more .wsm
files, with the configuration for each 4GL program and internal entry exposed as a SOAP operation. FWD will process all these files, automatically configure the 4GL program/internal entry as a SOAP operation, create the mappings and schema for the parameters, messages, bindings and port type, and generate the WSDL.
The result will be annotations at the converted Java code for the 4GL programs/internal entries and also .wsdl
files for each .wsm
file used as input.
Configuration¶
In FWD, thesoap-cfg
configuration parameter in p2j.cfg.xml
is used to specify either a comma-separated list of:
.wsm
files- folders (read recursively) containing
.wsm
files.
A sample of this parameters is:
<parameter name="soap-cfg" value="cfg/fwd.wsm" />
Conversion Process¶
When converting a 4GL application configured with SOAP, FWD will identify all 4GL programs (and its internal entries) exported as SOAP operations, from the .wsm
file. For each program or internal entry exposed as a SOAP operation, it will emit a LegacyService
Java annotation.
All the .wsm
file information is loaded during conversion via ServiceSupport.createSoapConfig
- this creates SoapConfig
instances for each .wsm
file, with SoapOperation
instances for each SOAP operation.
For each namespace
configured in the .wsm
file at DeploymentWizard/WebServiceNamespace
, a WSDL file will be generated defining all SOAP operations in that namespace.
Any 4GL program which itself or one of its internal procedures/functions exported as a SOAP service will have added in its name_map.xml
definition the with-services="true"
flag, at the class-mapping
node associated with the 4GL program.
When there are SOAP services configured at conversion, WSDL generation requires all programs/classes marked for SOAP services to be processed, regardless if this is a full or incremental conversion (for incremental conversion, only this phase will include all 4GL programs/classes configured for SOAP).
Otherwise, for incremental conversion, any 4GL program/class configured for SOAP will have its Java annotation emitted. If any .wsm
file is changed, a full conversion is required.
annotations/legacy_services.rules
, which:
- uses
ServiceSupport.getSoapOperations
, to determine the entire list of SOAP operations which are associated with this top-level block (4GL program, internal procedure, function, etc). - injects a
LegacyService
annotation for each SOAP operation, with itsnamespace
,binding
and operationname
.
convert/soap_services.xml
, with:
ServiceSupport.getSoapOperations
determines all SOAP operations associated with a top-level block (external program, internal procedure, etc).ServiceSupport.addSoapTableParameter
will register with the WSDL atable
parameter for that SOAP operation.ServiceSupport.addSoapDataSetParameter
will register with the WSDL adataset
parameter for that SOAP operation.ServiceSupport.addSoapParameter
will register with the WSDL a normal parameter for that SOAP operation.ServiceSupport.setSoapReturnType
will register with the WSDL for that SOAP operation the function's return type.
The internal representation (at conversion) of the WSDL is done via SoapWsdl
instances, for each SoapConfig
instance associated with a .wsm
file. Once all SOAP-related files are processed by convert/soap_services.xml
, it will call ServiceSupport.generateSoapWsdl
, which will write the WSDL files to disk, in the root package configured via pkgroot
parameter in p2j.cfg.xml
.
Generated Annotations/WSDL¶
Each 4GL program, class or internal entry will have aLegacyService
annotation emitted, like this:@LegacyService(type = "SOAP", namespace = "urn:tempuri-org", name = "pipe_integer", binding = "fwd")
where:
type = "SOAP"
marks it as a SOAP operationnamespace
defines the namespace (the target WSDL) for this operation- the operation
name
- the WSDL
binding
describing the SOAP operation messages (how the request and response are mapped to the 4GL parameters).
The WSDL files are generated for each namespace
(configured in the .wsm
file(s)); these WSDL files are generated in the pkgroot
folder, and must be included in the application jar, during build.
Runtime Invocation¶
At FWD server startup, it will go through all programs configured in name_map.xml
having with-services="true"
attribute, and will check their LegacyService
annotation - if type = "SOAP"
, it will automatically configure this program, class or internal entry as a SOAP operation.
Context handlers will be created for each defined service/port/address
in the WSDL files, and a handler for a certain endpoint will process all requests for the SOAP operations configured for it. All context handlers will have as handler a SoapHandler
instance, in FWD. One context handler (configured for an endpoint) can service SOAP operations defined in one or more WSDLs.
The last step for initializing the FWD SOAP service support is to launch a dedicated pool of LegacyServiceWorker
worker threads (via AppServerConnectionPool
), which only handle SOAP requests: each worker thread will be connected to the appserver (running in the same FWD server) responsible for performing the actual execution of SOAP call's target. Each LegacyServiceWorker
worker will have a AppServerHelper
instance, connected via a local proxy to the target appserver (running in the same JVM).
SoapHandler.handle
API is responsible for interpreting it; it will look at the SOAP envelope, and will:
- use the namespace to find the target WSDL definition this SOAP operation.
- look through the WSDL operations and find the target 4GL program, class or internal entry, for the SOAP operation.
SoapHandler.invoke
job, and wait for its completion. The worker will:
- parse the arguments from the SOAP envelope and create compatible instances with the target parameters (at the 4GL program or internal entry).
- use the SOAP service type to determine the proper
AppServerHelper
API to perform the remote call (i.e. invoke a 4GL program, an internal entry, etc), so it can delegate the call to aAppServerHelper.invoke...
method, where:- arguments are preprocessed via
AppServerHelper.preProcessParameters
AppServerHelper.invoke...
delegates the call to itsAppServerEntry
instance, which is a local proxy for theAppServerManager
static methods.
AppServerHelper.checkResults
will raise any exception as received from the remote side, and will post-process any OUTPUT/INPUT-OUTPUT arguments viapostProcessResult
method.
- arguments are preprocessed via
- the worker waits for the result from the
AppServerHelper.invoke
call - once the result is received, the worker will serialize the OUTPUT parameters in the SOAP response
When control is received back by the web thread handling the SOAP request, the worker thread will have finished the request invocation and processing of the HTTP response.
On server side, the remote request fromSoapHandler
is received by one of the Dispatcher
threads, which will:
- execute the proper
AppServerManager.invoke...
method. This will determine the proper FWD Agent to handle this request, post the work to the Agent (via one of itsinvoke...
methods), and wait for a response - the Agent's
invoke...
method will rely on one of theControlFlowOps
methods to execute the request - the call is executed as any other external program/internal procedure or function invocation, from the converted code. - The response is returned back to the
AppServerHelper
caller, via theAppServerManager.invoke
method.
WebHandler¶
Input Data¶
In 4GL, web handlers are configured in theopenedge.properties
file. From this file, all sections with the .WEB
suffix are read:[oepas1.goldencode.WEB] handler1=FwdHandler: /fwdHandler/{id}/{value} handler2=FwdHandler: /fwdHandler/{id} handler3=FwdHandler: /fwdHandler
where the syntax of a handler is:
handler#=<4GLClassName>: /path1/path2/.../
and:
handler#
specifies the priority of this path<4GLClassName>
specifies a 4GL class inheritingOpenEdge.Web.WebHandler
/path1/path2/.../
specifies the path handled by this handler
Configuration¶
FWD will use as input a single openedge.properties
file, specified via the webhandler-cfg
parameter in p2j.cfg.xml
:
<parameter name="webhandler-cfg" value="cfg/openedge.properties" />
Conversion Process¶
The information about the exposed web handler classes is resolved via ServiceSupport.resolveWebHandlers
. This creates a WebHandlerConfig
instance for each web handler, with its details (path, property section from where it was loaded, 4GL class, etc).
All 4GL classes specified as 'web handlers' will be identified during annotations, and each class AST will be added a 4GL-style annotation to idenfy this as a WEB handler, with its target paths. In 4GL, this annotation would look like this:
@LegacyService(type = "WEBHANDLER", name = "oepas1.goldencode.web", paths = [ @LegacyWebPath(order = 1, path = "/fwdHandler/{id}/{value}"), @LegacyWebPath(order = 2, path = "/fwdHandler/{id}"), @LegacyWebPath(order = 3, path = "/fwdHandler") ]).
This 4GL annotation can be added explicitly to any 4GL class inheriting OpenEdge.Web.WebHandler
, and will be automatically converted to a Java annotation, by the FWD conversion.
Any incremental conversion whichi includes a WebHandler class will automatically generate the proper annotation. Any changes in the configured .properties file related to the WEB
sections will require a full conversion.
annotations/legacy_services.rules
, which uses:
ServiceSupport.isWebHandler
to determine if a 4GL class is exposed as a web handler.- if
isWebHandler
is true, a newLegacyService
4GL-style annotation will be injected directly into the AST for this 4GL class, with the web handler's details. Helper APIs are:ServiceSupport.getSection
, to resolve the property file section where this web handler was defined, and emitLegacyService.section
annotation.ServiceSupport.getPaths
, to resolve the paths handled by this web handler, and emitLegacyService.paths
annotation.
Generated Annotations¶
Any WebHandler sub-class which is configured for web services in theopenedge.properties
file will automatically be added a LegacyService
Java annotation, like this:@LegacyService(name = "oepas1.goldencode.web", type = "WEBHANDLER", paths = { @LegacyWebPath(path = "/fwdHandler/{id}/{value}", order = 1), @LegacyWebPath(path = "/fwdHandler/{id}", order = 2), @LegacyWebPath(path = "/fwdHandler", order = 3) })
where:
type = "WEBHANDLER"
represents a legacy web servicename
represents the name of the section in the .properties file where this was configuredpaths
represents the handled paths by this class, viaLegacyWebPath
annotations, with:order
processing order.path
the handled path.
Runtime Invocation¶
At FWD server startup, it will go through all programs configured in name_map.xml
having with-services="true"
attribute, and will check their LegacyService
annotation - if type = "WEBHANDLER"
, it will automatically configure the legacy web service and map it to the WebHandler
implementation class. All legacy web services will be handled in FWD by the WebServiceHandler
class.
The last step for initializing the FWD legacy web service support is to launch a dedicated pool of LegacyServiceWorker
worker threads (via AppServerConnectionPool
), which only handle REST requests: each worker thread will be connected to the appserver (running in the same FWD server) responsible for performing the actual execution of the web service call's target. Each LegacyServiceWorker
worker will have a AppServerHelper
instance, connected via a local proxy to the target appserver (running in the same JVM).
WebServiceHandler.handle
API is responsible for interpreting it; if it finds a registered WebHandler
class for that path, it will post a task to a worker, and wait for its completion. The worker will:
- use
AppServerHelper.handleWebService
API to perform the remote call - the target legacy 4GL converted class will be included as a parameter.AppServerHelper.handleWebService
delegates the call to itsAppServerEntry
instance, which is a local proxy for theAppServerManager.handleWebService
static method.- the remote
handleWebService
call will receive theHttpServletRequest
andHttpServletResponse
Java instances.
- the worker waits for the result from the
AppServerHelper.handleWebService
call - once the result is received, the worker will serialize the response to the proper format/target (response body, cookie, header, etc).
When control is received back by the web thread handling the web request, the appserver Agent will have finished the request invocation and processing of the HTTP response.
On server side, the remote request fromWebServiceHandler
is received by one of the Dispatcher
threads, which will:
- execute the
AppServerManager.handleWebService
method. This will determine the proper FWD Agent to handle this request, post the work to the Agent (via onehandleWebService
method), and wait for a response. - the Agent's
handleWebService
method will:- initialize the FWD implementation of
OpenEdge.Web.WebRequest
to have access to theHttpServletResponse
andHttpServletResponse
instances. - create a new instance of the
WebHandler
implementation class and invoke itshandleRequest
method.
- initialize the FWD implementation of
© 2004-2022 Golden Code Development Corporation. ALL RIGHTS RESERVED.