Sharing Data between Virtual Service Models

Document ID : KB000048459
Last Modified Date : 14/02/2018
Show Technical Document Details

Sometimes you need to share non-persistent data across virtual services. See also PersistentModelMap.

Example 1

A classic example is that service A needs to create a new user account ID and service B needs to know what that account ID is in order to respond to, say, a credit check where the request contains the user name but not the ID.

One VSM can do something like this:


{{==com.itko.lisa.vse.SharedModelMap.put("accountNameToId", userName, ACCOUNT_ID);}}

and the other VSM can retrieve it as follows:


{{==com.itko.lisa.vse.SharedModelMap.get("accountNameToId", userName);}}

The first parameter to get/put is the optional namespace. It is optional but recommended so that there are no clashes of key names.

Example 2

Perhaps another example might help.

Model A might update an account balance like this:


{{=com.itko.lisa.vse.SharedModelMap.put("AccountBalance", accountId, 125);}}

and then Model B might retrieve it like this:


{{=com.itko.vse.SharedModelMap.get("AccountBalance", accountId);}}

The above implies you are directly editing a response meta transaction but you can also use the JavaScript step to directly access the SharedModelMaps and the testExec ("state").

If you want to remove an entry, call SharedModelMap.remove(namespace, key).

Each unique namespace is backed by a map that is restricted to a default capacity of 256 key/value pairs. Map entries are recycled in a LRU fashion.

If you need more capacity you can call :


SharedModelMap.setCapacity(namespace, newCapacity)	

SharedModelMap is thread-safe - two VSMs can access and update it at the same time (internally, access to each namespace map is synchronized on the map itself).

The maps are not persisted across VirtualEnvironmentService restarts. See PersistentModelMap for a persistent map implementation.

Of course you can use a JDBC step to get/set data from a permanent store, or write your own class that does something similar to SharedModelMap but permanently stores the data in a database or a memcached instance.


package com.itko.lisa.vse;
/**
 *
 * Maintains a statically shared set of maps, meant for sharing data across virtual service models at runtime in a
 * single JVM. Think of it as you would shared memory between processes in a traditional OS.  Maps are accessed
 * by namespace and key in most cases. If you are feeling lazy you can use the default namespace but this is not
 * encouraged, it's better to agree on a namespace that two or more VSMs can share that is meaningful to those VSMs.
 * 

* The get and put operations have String and Object variations with the assumption that most of the time you are * storing and retrieving String values. Keys must be strings. * * Each namespace has an LRU map created on demand the first time the namespace is used. It's backed by an * org.apache.commons.collections.map.LRUMap, see the javadoc there for details. * * All methods are thread-safe. */ public class SharedModelMap { public static final int DEFAULT_CAPACITY = 256; // ==================================================================== // These methods work with the default namespace. // ==================================================================== /** * This method returns the number of entries in the default namespace. * * @return the number of entries in the default namespace. */ public static int size() /** * This method returns whether the default namespace is empty or not. * * @return {@code true} if the default namespace is empty or {@code false} if not. */ public static boolean isEmpty() /** * This method returns whether the default namespace contains an entry with the given key * or not. * * @return {@code true} if the default namespace contains the specified key or {@code * false} if not. */ public static boolean containsKey(final String key) /** * This method returns whether the default namespace contains an entry with the given value * or not. * * @return {@code true} if the default namespace contains the specified value or {@code * false} if not. */ public static boolean containsValue(final Object value) /** * This method gets a value from the default namespace. * * @param key the key to the desired value. * @return the value known by the key or {@code null}. */ public static Object getObject(final String key) /** * This method gets a value from the default namespace cast as a string. * * @param key the key to the desired value. * @return the value known by the key or {@code null}. */ public static String get(final String key) /** * This method puts a value into the default namespace. * * @param key the key to make the value known by. * @param value the value to associate with the key. * @return the previous value for the key or {@code null}. */ public static Object putObject(final String key, final Object value) /** * This method puts a value into the default namespace cast as a string. * * @param key the key to make the value known by. * @param value the value to associate with the key. * @return the value known by the key or {@code null}. */ public static String put(final String key, final String value) /** * This method removes a value from the default namespace. * * @param key the key to the value to remove. * @return the value known by the key or {@code null}. */ public static Object remove(final String key) /** * This method gets a value from the default namespace cast as a string. */ public static void clear() /** * This method returns the set of keys currently known in the default namespace. Note that * the set returned, unlike the standard map semantics, is detached from the source map; * this helps us ensure thread-safety. * * @return the set of known keys in the default namespace. */ public static Set<String> keySet() // ==================================================================== // These methods work with a specified namespace. // ==================================================================== /** * This method returns the number of entries in the specified namespace. * * @param namespace the namespace to get the size of. * @return the number of entries in the specified namespace. */ public static synchronized int size(final String namespace) /** * This method returns whether the specified namespace is empty or not. * * @param namespace the namespace to get the empty state for. * @return {@code true} if the specified namespace is empty or {@code false} if not. */ public static boolean isEmpty(final String namespace) /** * This method returns whether the specified namespace contains an entry with the given key * or not. * * @param namespace the namespace to check for key containment. * @return {@code true} if the specified namespace contains the given key or {@code false} * if not. */ public static synchronized boolean containsKey(final String namespace, final String key) /** * This method returns whether the specified namespace contains an entry with the given * value or not. * * @param namespace the namespace to check for value containment. * @return {@code true} if the default namespace contains the specified value or {@code * false} if not. */ public static synchronized boolean containsValue(final String namespace, final Object value) /** * This method gets a value from the specified namespace. * * @param namespace the namespace to get the value from. * @param key the key to the desired value. * @return the value known by the key or {@code null}. */ public static synchronized Object getObject(final String namespace, final String key) /** * This method gets a value from the specified namespace cast as a string. * * @param namespace the namespace to get the value from. * @param key the key to the desired value. * @return the value known by the key or {@code null}. */ public static String get(final String namespace, final String key) /** * This method puts a value into the specified namespace. * * @param namespace the namespace to put the value in. * @param key the key to make the value known by. * @param value the value to associate with the key. * @return the previous value for the key or {@code null}. */ public static synchronized Object putObject(final String namespace, final String key, final Object value) /** * This method puts a value into the specified namespace. * * @param namespace the namespace to put the value in. * @param key the key to make the value known by. * @param value the value to associate with the key. * @return the previous value for the key or {@code null}. */ public static String put(final String namespace, final String key, final String value) /** * This method removes a value from the specified namespace. * * @param namespace the namespace to remove the key from. * @param key the key to the value to remove. * @return the value known by the key or {@code null}. */ public static synchronized Object remove(final String namespace, final String key) /** * This method gets a value from the specified namespace cast as a string. * * @param namespace the namespace to clear. */ public static synchronized void clear(final String namespace) /** * This method returns the set of keys currently known in the specified namespace. Note * that the set returned, unlike the standard map semantics, is detached from the source * map; this helps us ensure thread-safety. The value returned will never be {@code null}. * * @param namespace the namespace to get the set of keys for. * @return the set of known keys in the default namespace. */ public static synchronized Set keySet(final String namespace) /** * This method may be used to resize the capacity of the specified namespace. * * @param namespace the namespace to change the capacity for. * @param newCapcity the new capacity for the namespace. */ public synchronized static void setCapacity(final String namespace, final int newCapcity) }