You might want to consider the following flow to see if it meets your requirements.
The key is that the Responder step is only going to fire one (1) time and then close the original socket.
This snippet represents just one approach. I typed the code pretty quickly so please expect potential syntax errors.
1) VSI Selection Step
a) Add a scripted Assertion or Add a JSR-223 step between the VSI Selection step and the Responder step.
b) If assertion, set the Assertion to only fail if the response is 'false'. If JSR is used, you can change to return string and assert on that.
Potential logic:
import com.itko.lisa.vse.stateful.model.TransientResponse;
import java.util.List;
// get the list of responses after the VSI Selection Step occurs
List lst = testExec.getStateValue("lisa.vse.response");
// remove the first response from the list because it
// will be sent by the Responder step automatically
lst.remove(0);
// Save off the remaining responses - they will be checked later during the
// looping process that sends the subsequent responses
testExec.setStateObject("fl_responseList", lst);
// return true so the script does not fail. If using JSR-223 change to return "true" if you want
return true;
2) Responder Step - leave in place and let if fire as it normally would
3) Add a JSR-223 step to simulate a thread sleep for 5 seconds
Thread.sleep(5000);
4) After the sleep step, add a JSR-223 Step to process the callback transaction from the saved list.
import com.itko.lisa.vse.stateful.model.TransientResponse;
import java.util.List;
// Get the List that was saved off in the VSI Response Selection Step
List lst = (List)testExec.getStateObject("fl_responseList");
// Add an Assertion to this step - when "false", go to LISTEN step
if (lst.size() == 0) {
return "false"; // List size 0 means no more to send
}
// List size > 0 means we need to send a response
TransientResponse tr = lst.get(0);
String rsp = tr.getBodyAsString();
testExec.setStateValue("fl_asyncResponse", rsp);
// remove the response from the list so it is not sent again
lst.remove(0);
// store the LIST -- it will be checked after the loop occurs
testExec.setStateObject("fl_response_list", lst);
// return true. Use an Assertion to stop the loop and go to LISTEN
return "true";
Add an Assertion to step 4). If the response is "false", branch to LISTEN Step. Otherwise, keep going.
5) Add a REST or WS Execution XML step. In the body (content) use DevTest notation to reference the response body from the list
{{fl_asyncResponse}}
Using the For Next Step, branch the REST or WS Execution XML step to the Thread Sleep step ( i.e., Step 3 ) if you want to wait 5 seconds before sending the next response. Or, branch to Step 4, to immediately setup for the next response to go on the wire.
You can get more fancy by adding code to access response header meta data if there are certain values that need to go out on the response (REST request) header.
NOTES:
- The above example does not take into account any knowledge of the behavior of the actual application.
- The above example shows an HTTP 1.1 callback flow not a 2.0 flow.
- The async responses are technically requests from the VSM to an application. They are not unsolicited responses to an earlier request.
- The VSM's REST or WS Exec step will wait for a response before continuing. If an ACK or other response is not sent, an HTTP timeout is likely to occur.
- Because of the callback and thread sleeps, a functional VSE may experience performance issues during periods of heavier load as TPS is throttled.
- This is just one approach for looping and sending callbacks. One could implement a delegation model and have a secondary VSM provide the callback workload. A mechanism to pass the responses (e.g., via the Shared Model Map) to the second VSM would be required. This approach is more complex and will not be addressed here.
Hope this helps.