Overview and Purpose

The Problem

In certain environments and certain situations, long running SOAP requests cause problems.

The problems may be related to changes in the TCP/IP stack introduced with the Windows versions mentioned above, however, this has not yet been verified and there are other reasons, why connection should not be kept opened for that long time.
More information can be found here:
SynAttackProtect not suggested on Win 2008 /Win 2008 R2 
According to this article link http://msdn.microsoft.com/en-us/library/ee377058(BTS.10).aspx
The SynAttackProtect, TcpMaxHalfOpen, and TcpMaxHalfOpenRetried registry entries are no longer used with Windows Vista and Windows Server 2008. The TCP/IP protocol suite implementation in Windows Vista and Windows Server 2008 was redesigned to provide improved performance and does not require manual modification of these registry entries.
Our proposal to workaround these problems is:

Status and Outlook

Currently, asynchronous requests are in testing and evaluation phase. There is some handwork required to enable asynchronous calls for placeholders or build scripts, also there are some limitations, which will disallow using asynchronous requests in certain situations.

On the server side, we use the EJB 3.1 @Asynchronous features to enable asynchronous requests.
On the client side, there is no native support for asynchronous requests (the SOAP library linked with priint:comet ID and priint:renderer does not support asynchronous communication), so we have to manually implement a polling mechanism to check if the result for a request is ready.

At the time, there is no cscript support for this polling either, so we have to implement this individually for each placeholder as described below.
In future versions there may be cscript functions to execute asynchronous requests or even configuration properties on the ISON side to switch placeholders to asynchronous communication.

Requirements

You need

Asynchronous Calls Implementation

The AsynchronousPubServerPlugin provides a couple of Java / cscripts functions, which allow to implement asynchronous requests in cscript.
The basic functionality is:

Example Cscript

#pragma var "search//Search"
#pragma var "replace//Replace"

#include "[pubserver]/plugin/com.priint.pubserver.comet.bridge.async.CometAsyncSoapRequest.c"

int main() {
    int ready = 0;
    int i = 0;
    StringList result;
    char * ticketId = 0;
    char * command = alloc (4096);

// build command
//
// the original load statement for this placeholder was
//         #pragma var "search//Search"\
//  
//         #pragma var "replace//Replace"\
//         call stringlist plugin(globalName='DataProviderManager',methodName='get')\
//         [<Session.Id>,'BucketLabel',<Model.Id>,<Entity.Id>,\
//         'product',<Record.Id>,<Record.GroupId>,'','','','','',
//         <xml.publication>,'','',<xml.documentId>,\
//         {'<Variable.search>' => <search>,'<Variable.replace>' => <replace>}, \
//         <Java.ResultClass>]:placeholder#268435763#get\
    strcpy(command, "plugin(globalName='DataproviderManager',methodName='get')");
    strcpy(command, "[<Session.Id>,'BucketLabel',<Model.Id>,'");

// the record Ids must be inserted from the global cscript variables
//   -> mind the 'quotes'!
    strcat(command, pPSEntityId);
    strcat(command, "','product','");
    strcat(command, gPSRecordId);
    strcat(command, "','");
    strcat(command, gPSGroupId);
    strcat(command, "','','','','','',<xml.publication>,'','',<xml.documentId>,");

// for function variables, the global cscript variables must be used also:
//   (again: 'quoted'!)
    strcat(command, "'{'<Variable.search>' => '";);
    strcat(command, search);
    strcat(command, ','<Variable.replace>' => '";);
    strcat(command, replace);

    strcat(command, "'},<Java.ResultClass>]:placeholder#268435763#get");

// start request
    ticketId = asyncCall(command);
  
// poll for ready state
    for (i = 0; i < 10000; ++i) {
        ready = isResultReady(ticketId);
        wlog ("", "Result ready? %d\n", ready); 
        if (ready) break;
        system::sleep(1);
    }
  
    if (!ready) return 1;

// fetch result
    result = getResultAsStrings(ticketId);
    wlog ("", "Result size: %d\n", stringlist::length(result));
    for (i = 0; i < stringlist::length(result); ++i) {
        showmessage("Result %d: %s", i, stringlist::get(result, i));
    }
  
    // clean up
    if (command != 0) release(command);
    if (result != 0) stringlist::release(result);
    if (ticketId != 0) release(ticketId);
    return 0;
}
  

Example Explanation

This is an exmaple for a simple placeholder call, which will be executed asynchronous (the result is just shown in a message box, it could as well be inserted in the document using textmodel::replace or similar functions).
The statement sent with the asyncCall method is derived from the original placeholder statement, which looks like this:

#pragma var "search//Search"

#pragma var "replace//Replace"

call stringlist plugin(globalName='DataProviderManager',methodName='get')[<Session.Id>,'BucketLabel',<Model.Id>,<Entity.Id>,'product',<Record.Id>,<Record.GroupId>,'','','','','',<xml.publication>,'','',<xml.documentId>,{'<Variable.search>' => <search>,'<Variable.replace>' => <replace>}, <Java.ResultClass>]:placeholder#268435763#get
  
Embedding this statement in a cscript requires some adaption:
  1. the #pragma var section does not have to be copied. This part is generated by the CometBridge, no matter, if the statement is a "direct" SOAP statement or a cscript
  2. the actual statement consists of three terms:
    • Statement type: call or getlist
      -> depending on the type, you will use the asyncCall or asyncGetList function provided by the AsynchronousPubServerPlugin
    • Result type in this case stringlist
      -> can be omitted, because the result type is determined by the getResultAs... call at the end.
    • the Statement interpreted by priint:publishing server, in most cases starting with the keyword plugin
      -> This term has to be provided as argument for the asyncCall resp. asyncGetList function call.

Once again the original statement with comments

#pragma var "search//Search"

#pragma var "replace//Replace" // omit

call                           // call=use asyncCall, getlist=use asyncGetList
stringlist                     // omit
plugin(globalName='DataProviderManager',methodName='get')[<Session.Id>,'BucketLabel',<Model.Id>,<Entity.Id>,'product',<Record.Id>,<Record.GroupId>,'','','','','',<xml.publication>,'','',<xml.documentId>,{'<Variable.search>' => <search>,'<Variable.replace>' => <replace>}, <Java.ResultClass>]:placeholder#268435763#get
                            // process like shown above and use 
                            // as argument for the async... function call
  
The second step is waiting for the request to be ready. In this case, we use a for loop and wait for one second, at maxiumum for 1000 times - which effectively means a timeout of 16 minutes and 40 seconds.
At the time, there is no way to abort a call. If we give up waiting in the cscript, the server still continues processing and will preserve the result, until it is requested by the client.

Finally, if the server reported success, we fetch the result using one of the fetch... methods provided by the AsynchronousPubServerPlugin.

Step by Step: from Sync to Async

The general procedure to change from synchronous to asynchronous requests is:

API / Function Overview

asyncCall

char* asyncCall(char* statement);
Submit a asynchronous call request. Use this function for simple return data types, such as placeholder load statements (which typically return a StringList) etc.
On success, the function returns a ticketId, which can be used for subsequent isResultReady and fetch... calls.

asyncGetList

char* asyncGetList(char* statement);
Submit a asynchronous getList request. Use this function for complex return data types, such as product lists (e.g. findstatements) or RecordIds (e.g. table insert scripts).
On success, the function returns a ticketId, which can be used for subsequent isResultReady and fetch... calls.

fetchResultAsRecordIds

IDTypeList fetchResultAsRecordIds(char* ticketId);
Fetch the result of a request as a list of RecordIds (cscript type idtype). This function must not be called, before the isResultReady call for this ticketId returns true.

fetchResultAsStrings

StringList fetchResultAsStrings(char* ticketId);
Fetch the result of a request as a list of Strings (cscript type string). This function must not be called, before the isResultReady call for this ticketId returns true.

fetchResultAsProducts

ProductList fetchResultAsProducts(char* ticketId);
Fetch the result of a request as a list of Products (cscript type product). This function must not be called, before the isResultReady call for this ticketId returns true.

isResultReady

int isResultReady(char* ticketId);
Check, if a request is ready. Note, that the function also returns true (1), if the result finished with an error.