I believe this topic is going to become lengthy because there may be more complexity than we see in the original post.
The short answer is that your service might best work using a Stateful Model (Stateless and Conversational VSE Transactions - DevTest Solutions - 10.1 - CA Technologies Documentation) rather than Stateless model.
Before we look at the Shared Model Map, let's consider some things. Consider these assumptions. Changes in these assumptions affect virtual service implementation behavior:
a) The order of the calls must be as follows as per the original post:
getDetails (account 123 - 1st response)
maintainDetails (account 123)
getDetails (account 123 - 2nd response)
getDetails (account 123 - 1st response)
b) The order of the calls can not be as follows:
getDetails (account 123 - 1st response)
getDetails (account 123 - 1st response)
maintainDetails (account 123)
getDetails (account 123 - 2nd response)
c) The order of the calls can not be as follows:
getDetails (account 123 - 1st response)
maintainDetails (account 123)
getDetails (account 123 - 2nd response)
maintainDetails (account 123)
getDetails (account 123 - 3rd response)
-- repeat this process 'n' number of times
d) The order of the calls can not be as follows:
getDetails (account 123 - 1st response)
getDetails (account 123 - 1st response)
getDetails (account 123 - 1st response)
getDetails (account 123 - 1st response)
getDetails (account 123 - 1st response)
-- repeat this process 'n' number of times
[my point with the assumptions is that controlling state within a stateless model means the service may need to cater for variety of request / response scenarios and sequences. Establishing the behavioral characteristics supported by the service is critical to setting the expectations for what the service can and cannot do.]
In a stateless model, one approach is to consider the maintainDetails operation call as the 'triggering' event because it helps the service deal with the assumption scenarios above.
This means that the response to the getDetails transaction is the first response (regardless of the number of times the getDetails operation is invoked). Only a maintainDetails call can trigger the beginning of subsequent getDetails responses. This helps in those situations where getDetails is called, but maintainDetails is not part of the test scenario.
This also means that the number of times the maintainDetails call occurs may be used as a counter on which the VSI can select subsequent getDetails responses. We will see a high level implementation of this approach below.
The next thing to consider is how does the virtual service know when to start over?
The posted scenario depicts a simple pattern of getDetails (rsp 1) -> maintainDetails -> getDetails (rsp 2), and the implication is that the flow starts over after the maintainDetails call is made the first time. But, what happens if some response sequences need 3, 4, or 5 different responses to simulate multiple calls associated with maintainDetails? How does the service determine this and know when to reset the flow back to "1"? This is a critical issue that must be clearly understand because the setup of the VSI depends on it.
The remainder of this post examines the post in the context of the simple case (e.g., getDetails [rsp 1] -> maintainDetails -> getDetails [rsp 2], and then flow starts back at 1 with a couple of twists)
- Suppose the VSI could support an additional property as a request argument that is injected at run time. The VSI could use this argument as a counter enabling an additional comparison. If the VSM were to manually add this argument (let's call it devtest_seqNum) to the VSI on the getDetails transaction, the VSI could enable the concept of having multiple specific transactions for a given account number.
VSI View of GET /getDetails operation:
Spec Txn 1, Acct = 123, devtest_seqNum = 0, response is response 1
Spec Txn 2, Acct = 123, devtest_seqNum > 0, response is response 2
Spec Txn 3, Acct = ABC, devtest_seqNum = anything, always send this response
(Account = ABC never participates in sending answers other than 1 response)
Spec Txn 4, Acct = 789, devtest_seqNum = 0, response is response 1
Spec Txn 5, Acct = 789, devtest_seqNum = 1, response is response 2
Spec Txn 6, Acct = 789, devtest_seqNum > 1, response is response 3
(Account 789 participates in a scenario where two maintainDetails calls are made followed by a getDetails so we need additional responses)
- The JSR step or Scriptable DPH could then use the Model Map to store a combination of key=accountNumber / value=sequenceNum to keep track of how many times the maintainDetails operation has been called. The count of the number of times maintainDetails has been invoked for a given account number determines which VSI selection (above) happens.
- The trick then is to insert (or add) an additional argument to the incoming request's argument list when the getDetails call happens. This additional argument provides a counter on which the VSI can perform a lookup.
Potential psuedo code for this type of logic follows a pattern similar to the following:
if (request operation is NOT 'GET /getDetails' and 'POST /maintainDetails' )
exist JSR or Scriptable DPH step // nothing to do
// so the logic can add a new argument only on getDetails
ParameterList paramList = lisa_vse_request.getArguments();
if ( request operation = 'GET /getDetails' ) {
// account not on map means maintainDetails has not been called
if (NOT SharedModelMap.containsKey(<namspace>, <accountNum from lisa_vse_request>)
// no reason to add acct to map since maintainDetails has been made
// this also keeps the map as small as possible
add devtest_seqNum=0 to paramList
lisa_vse_request.setArguments( paramList )
else
// getDetails never increments the counter only uses the counter
// this enables multiple getDetails calls w/out a maintainDetails in the middle
add devtest_seqNum=SharedModelMap.get(<namspace>, <accountNum from lisa_vse_request>)
lisa_vse_request.setArguments( paramList )
exit JSR or Scriptable DPH step
}
if ( request operation = 'POST /maintainDetails' ) {
// not on the map means first call for this account
if (NOT SharedModelMap.containsKey(<namspace>, <accountNum from lisa_vse_request>)
SharedModelMap.put(<namespace>, <accountNum from lisa_vse_request>, "1")
add devtest_seqNum=1 to paramList
lisa_vse_request.setArguments( paramList )
else
String val = SharedModelMap.get(<namspace>, <accountNum from lisa_vse_request>)
// convert to int for math but OK to save as string so you don't have to deal
// no reason to store objects on the map unless you just want to
// this is where you might need to deal with resetting
// the counter - base this on the requirements for example
// if counter > 2
// SharedModelMap.removeKey(<namespace>, <accountNum from lisa_vse_request>)
// maybe you want to start the devtest_seqNum back to "0" and exit script
add 1 to the seq #
SharedModelMap.put(<namespace>, <accountNum from lisa_vse_request>, <incremented value>)
add devtest_seqNum=<incremented value toString> to paramList
lisa_vse_request.setArguments( paramList )
}
exist JSR or Scriptable DPH step
- Additionally, one needs to determine how to reset the map. What happens if a transaction becomes hung?
Shared Model Map memory is only cleared out by start/stop of VSE.
You might consider adding a custom, administrative operation to the service such as 'GET /getDetails/clearMap' and a custom step in the VSM that, when this operation is encountered, a step iterates the map (e.g., SharedModelMap.keySet(<namespace>) and removes all entries or 'GET /getDetails/clearMap?acct=1234' (e.g., SharedModelMap.remove(<namespace>, <accountNum from lisa_vse_request>) with logic to remove a specific account number from the map. And, 'GET /getDetails/displayMap' returns a list of each account number and its sequence number for the admin to view entries on the map. Admin approaches like these provide the developer a bit of administrative control over the map without taking services offline or shutting down VSE to clear the map.
Don't forget that by default, the Shared Model Map namespace only holds 256 entries to minimize the impact of in-memory storage. If you have an unlimited number of account numbers you might need to switch to Persistent Model Map.