Back to Contents


Stateful Web Services

Topics


Concept

A stateful service is a service that maintains a context between a web services client and server. It enables the service to keep trace of previous requests from that context, in order to manage different states in the web service server.

GWS supports two kinds of stateful services:

The Genero Web Service engine uses a 4GL variable defined at stateful service creation via CreateStatefulWebService() as service context. Use that variable to hold a service state in a database.

It is up to the 4GL programmer to create, store and remove the service state in the database.

The SOAP engine is responsible for:


WS-Addressing 1.0 stateful services

A stateful service based on WS-Addressing uses the WS-Addressing EndpointReference type as state variable and is independent from the transport layer used. (See WS-Addressing 1.0 EndpointReferenceType). The session state is conveyed from the client to the server as WS-Addressing 1.0 reference parameters.

Server side

Perform these steps to create a WS-Addressing stateful service:

1. Declare a W3CEndpointReference record to be used as state variable.

This record MUST have:

Example:
  DEFINE EndpointReferenceState RECORD ATTRIBUTE(W3CEndpointReference)
    address STRING, # Mandatory
    ref RECORD # Sub-record Reference parameters containing one or more state variables 
      OpaqueID String ATTRIBUTE(XMLName="OpaqueID"), # Unique ID to identify the service state in the database
      Expiration DATE ATTRIBUTE(XMLName="Expiration",XMLNamespace="http://tempuri.org") # Session state expiration date
    END RECORD
  END RECORD

You can use a unique ID of a database table to manage the web services sessions in place of OpaqueID.

2. Create a stateful WS-Addressing enabled web service with above W3CEndpointReference record as a parameter.

The Genero Web Service extension provides a new Web service constructor called CreateStatefulWebService() to perform stateful services. This function works as the stateless constructor, but expects a W3CEndpointReference record as parameter.

Example

  DEFINE serv com.WebService
  LET serv = com.WebService.CreateStatefulWebService("StatefulWSAddressingService","http://4js.com/services",EndpointReferenceState) # Create a stateful service with a W3CEndpointReference state variable
  CALL serv.setFeature("WS-Addressing1.0","REQUIRED") # enable support of WS-Addressing 1.0

3. Publish a web service operation returning the W3CEndpointReference state variable and set it as session initiator.

You must define which web service operation will initiate the session on your service and return the W3CEndpointReference state variable.

All other web service operations (not defined as session initiator) will return an error if they don't get reference parameters defined in the W3CEndpointReference state variable as WS-Addressing 1.0 headers.

Example

  DEFINE op com.WebOperation
  LET op = com.WebOperation.CreateDocStyle("GetInstance","GetInstance",NULL,EndpointReferenceState)
  CALL op.initiateSession(true)
  CALL serv.publishOperation(op,NULL)
There is no restriction regarding the input parameter of the web service initiator function, but the output parameter must be the same W3CEndpointReference record passed to the service creation constructor.

It is not required to have a web operation which initiate the session in the same service, but then you have to return the same W3CEndpointReference record in another web service to instantiate the session, such as a Factory service that instantiates all sessions for other stateful services.

4. Create the 4GL session initiator function and instantiate a new session.

In your 4GL function declared as session initiator, you have to :

Example

  FUNCTION GetInstance()
    LET EndpointReferenceState.address = NULL # Use default end point location
    LET EndpointReferenceState.ref.OpaqueID = com.Util.CreateUUIDString() # Generate an unique string (can come from a database table id)
    LET EndpointReferenceState.ref.Expiration = CURRENT  + INTERVAL HOUR TO HOUR (1) # Create expiration date in one hour to discard request after that date
      ... Store OpaqueID into database or use directly a database table entry to hold the session
  END FUNCTION

5. Restore the session in any 4GL web operation from the W3CEndpointReference record.

In any publish 4GL web function, the SOAP engine deserializes the WS-Addressing 1.0 reference parameter headers into the W3CEndpointReference sub-record so that you can retrieve the session from the state variable.

Example

  FUNCTION MyFunction()
    IF EndpointReferenceState.ref.OpaqueID IS NULL THEN
      CALL com.WebServiceEngine.SetFaultString("Invalid session id")
      RETURN 
    ELSE
      ... Restore the service session based on the OpaqueID state variable from the database
    END IF
      ... Process the operation
  END FUNCTION

Client side

Perform these steps to communicate with a stateful web service based on WS-Addressing 1.0:

1. Generate the client stub from your WS-Addressing stateful service.

Use the fglwsdl tool as usual. It will detect that the service returns a W3CEndpointReference and generate the appropriate code.

The WSDL imports the WS-Addressing 1.0 schema, so the fglwsdl tool requires an access to the W3C server. Use the option -proxy if you need to connect via a proxy server.

Example:

  $ fglwsdl -o ws_stub http://localhost:8090/StatefulWSAddressingService?WSDL
The generated .inc file contains a variable of type tWSAGlobalEndpointType to be used to transmit the WS-Addressing 1.0 reference parameters.

Example of a global variable name:

  DEFINE StatefulWSAddressingService_StatefulWSAddressingServicePortTypeEndpoint tGlobalWSAEndpointType

2. Create the MAIN application.

In your main application:

  1. Import the XML library. This is due to the support of WS-Addressing 1.0 with IMPORT XML.
  2. Import the generated .inc file with GLOBALS "ws_stub.inc"
  3. Manage the WS-Addressing 1.0 reference parameters representing the session state (if your client has to handle several instances of a same service).

Example

  IMPORT XML # Import the XML library required for WS-Addressing 1.0
  
  GLOBALS "ws_stub.inc" # Import service global definition
  
  TYPE InstanceType DYNAMIC ARRAY OF xml.DomDocument 	# End point WSA reference parameters
  
  DEFINE instance1,instance2,instance3 InstanceType # Store the different sessions the client will have to manage
  
  MAIN
    ...
  END MAIN

3. Instantiate a new session by calling the web service operation set as session initiator.

Call the 4GL function generated from the WSDL that is defined as session initiator on the server. This function returns a W3CEndpointReference parameter that contains the WS-Addressing 1.0 reference parameters representing the new instance created on server side.

If your application handles several instances, you will have to copy and store those parameters in your application to identify a service instance for further requests.

As the WS-Addressing 1.0 reference parameters are defined as any XML document, they are represented as a dynamic list of xml.DomDocument in 4GL.

Example

  DISPLAY "Creating a new instance ..."  
  LET wsstatus = GetInstance_g() # call the service session initiator web function
  IF wsstatus == 0 THEN
    FOR ind=1 TO ns1GetInstanceResponse.return.ReferenceParameters._LIST_0.getLength()
      LET instance1[ind]=ns1GetInstanceResponse.return.ReferenceParameters._LIST_0[ind].clone() # copy the service returned WS-Addressing 1.0 reference parameters
    END FOR
  ELSE
     ... handle soap errors
  END IF
When creating a new instance, ensure that the Parameters member of the generated global variable of type tWSAGlobalEndpointType has been set to NULL, otherwise the server will complain.

4. Call any web service operation with previously returned WS-Addressing 1.0 reference parameters.

Before calling any web service operation, you must set the WS-Addressing 1.0 reference parameters returned by a session initiator function to identify the session to the server.

Example

  LET StatefulWSAddressingService_StatefulWSAddressingServicePortTypeEndpoint.Address.Parameters.* = instance1.* # assign WS-Addressing 1.0 reference parameters dynamic array by reference
  CALL MyFunction("Hello") RETURNING wsstatus,ret  # Call web operation MyFunction of instance 1 

Stateful services based on HTTP Cookies

A stateful service based on HTTP cookies uses the HTTP transport protocol and its ability to convey cookies, used as session context. Notice that it works only if the communication path between the client and the server is performed in HTTP, otherwise it is recommended to use WS-Addressing stateful services.

Server side

Perform the following steps to create an HTTP cookie based stateful service.

1. Declare any 4GL simple variable to be used as state variable.

Example

  DEFINE ServiceState STRING # Unique ID to identify the service state in the database
For instance, you can use a unique ID of a database table to manage the web services sessions.

2. Create a stateful web service with above state variable as parameter.

The Genero Web Service extension provides a new Web service constructor called CreateStatefulWebService() to perform stateful services. This function works as the stateless constructor, but expects a simple state variable as parameter.

Example

  DEFINE serv com.WebService
  LET serv = com.WebService.CreateStatefulWebService("StatefulCookieService","http://4js.com/services",ServiceState) # Create a stateful service with a simple 4GL variable as state variable

3. Publish a web service operation defined as session initiator.

Define which web service operation will initiate the session on your service and instantiate a new session.
All other web service operations (not defined as session initiator) will return an error if they don't get an HTTP cookie called GSESSIONID.

Example

  DEFINE op com.WebOperation
  LET op = com.WebOperation.CreateDocStyle("GetInstance","GetInstance",NULL,NULL)
  CALL op.initiateSession(true)
  CALL serv.publishOperation(op,NULL)
There is no restriction on the web service session initiator function regarding to the input and output parameters.

4. Create the 4GL session initiator function and instantiate a new session.

In your 4GL function declared as session initiator, you must:

Example

  FUNCTION GetInstance()
    LET ServiceState = com.Util.CreateUUIDString() # Generate an unique string (can come from a database table id)
      ... Store ServiceState value into database or use directly a database table entry to hold the session
  END FUNCTION

5. Restore the session in any 4GL web operation from the state variable.

In any publish 4GL web function, the SOAP engine deserializes the HTTP Cookie called GSESSIONID from the HTTP layer into the state variable. You can then retrieve the session in 4GL via that state variable.

Example

  FUNCTION MyFunction()
    IF ServiceState IS NULL THEN
      CALL com.WebServiceEngine.SetFaultString("Invalid session id")
      RETURN 
    ELSE
      ... Restore the service session based on the ServiceState variable from the database
    END IF
      ... Process the operation
  END FUNCTION

6. Deployment recommendation.

When deploying stateful web services based on HTTP cookies, the complete server path will be added into the cookie when first instantiated, so you must pay attention to that URL. In other words, you MUST always call the service via the complete URL containing the service name inside. For instance if your service is named MyService and if you GAS configuration file is called Server.xcf, the stateful service is accessible at URL : http://localhost:6394/ws/r/group/Server/MyService.

Client side

Perform the following steps to communicate with a stateful web service based on HTTP cookies.

1. Generate the client stub from your stateful service.

Use the fglwsdl tool as usual.

Example:

  $ fglwsdl -o ws_stub http://localhost:8090/StatefulCookieService?WSDL
The generated .inc file contains a variable of type tGlobalEndpointType to be used to transmit the HTTP Cookie.

Example of a global variable name:

  DEFINE StatefulCookieService_StatefulCookieServicePortTypeEndpoint tGlobalEndpointType

2. Create the MAIN application.

In your main application:

Example

  GLOBALS "ws_stub.inc" # Import service global definition
    
  DEFINE instance1,instance2,instance3 String # Store the different sessions the client will have to manage in a string
  
  MAIN
    ...
  END MAIN

3. Instantiate a new session by calling the web service operation set as session initiator.

Call the 4GL function generated from the WSDL that was defined as session initiator on the server. This function returns a new HTTP Cookie saved into the Binding.Cookie member of the global service variable of type tGlobalEndpointType. If your application handles several instances, you will have to copy and store that cookie in your application to identify a service instance for further requests.

Example

  DISPLAY "Creating a new instance ..."  
  LET wsstatus = GetInstance_g() # call the service session initiator web function
  IF wsstatus == 0 THEN
    LET instance1 = StatefulCookieService_StatefulCookieServicePortTypeEndpoint.Binding.Cookie # copy the service returned HTTP cookie
  ELSE
     ... handle soap errors
  END IF
When creating a new instance, ensure that the Binding.Cookie member of the generated global variable of type tGlobalEndpointType has been set to NULL, otherwise the server will complain.

4. Call any web service operation with previously returned HTTP cookie.

Before calling any web service operation, set the HTTP cookie returned by a session initiator function to identify the session to the server.

Example

  LET StatefulCookieService_StatefulCookieServicePortTypeEndpoint.Binding.Cookie = instance1 # use instance1
  CALL MyFunction("Hello") RETURNING wsstatus,ret  # Call web operation MyFunction of instance 1 

5. Troubleshooting

If your Genero application doesn't set the HTTP cookie when accessing a stateful service via the GAS, it is possible that you didn't use the complete URL when accessing the service.
For instance if your service is named MyService and if you GAS configuration file is called Server.xcf, the stateful service is accessible at URL : http://localhost:6394/ws/r/group/Server/MyService.

Back to the top