Using the KeyValueStoreServices Object
The KeyValueStoreServices object lets you retrieve a key value store. Key value store is used to retrieve, add, edit, and delete key value pairs on the Gateway. A single key value store is shared between all custom assertions. To retrieve key value pairs associated with your custom assertion only, your key must be prefixed with a string that is unique to your custom assertion. Then, you can use this prefix to retrieve key values pairs for your custom assertion. For example, your custom assertion class name can be used as a prefix.
gateway
The
KeyValueStoreServices
object lets you retrieve a key value store. Key value store is used to retrieve, add, edit, and delete key value pairs on the Gateway. A single key value store is shared between all custom assertions. To retrieve key value pairs associated with your custom assertion only, your key must be prefixed with a string that is unique to your custom assertion. Then, you can use this prefix to retrieve key values pairs for your custom assertion. For example, your custom assertion class name can be used as a prefix. You are responsible for encoding and decoding the objects that you want to store/retrieve from the key value store. You should be aware of any issues with the specific encoding/decoding implementations (for example, XMLDecoder allows near-arbitrary code execution).
The following sample code illustrates how the
KeyValueStoreServices
features might be used in Custom Assertion Dialog class:public class SalesForceConnectorDialog extends JDialog implements AssertionEditor { private Map consoleContext; public SalesForceConnectorDialog(SalesForceConnectorCustomAssertion salesForceConnectorCustomAssertion, Map consoleContext) { ... this.consoleContext = consoleContext; this.init(); ... } private void init() { // Populate Salesforce Connections Combo Box. // Map<String, byte[]> connections = this.getKeyValueStoreUIServices().getKeyValueStore().findAllWithKeyPrefix(<prefix>); // Remove disabled connections. // List<String> keys = new LinkedList<String>(); for (Map.Entry<String, byte[]> entry : connections.entrySet()) { byte[] value = entry.getValue(); // Decode byte[] to Java object. SalesForceConnection connection = SalesForceConnectionUtils.fromBytes(value); if (connection.getIsEnabled()) { keys.add(entry.getKey()); } } // Sort by key name. // List<String> sortedKeys = new ArrayList<String>(keys); Collections.sort(sortedKeys); // Add to combo box. // for (String connectionKey : sortedKeys) { // Convert key to connection name. // The name is what is displayed in the UI. // The key is what is stored in the custom key value store. // The key should contain a prefix that is unique to your custom assertion. For example, the class name. String name = key.substring(<prefix>.length()); connectionComboBox.addItem(name); } } private void viewToModel(SalesForceConnectorCustomAssertion model) { ... // Convert connection name to a key. String name = connectionComboBox.getSelectedItem(); String key = <prefix>+<name>; model.setConnectionKey(key); ... } private void modelToView(SalesForceConnectorCustomAssertion model) { connectionComboBox.setSelectedItem(null); String key = model.getConnectionKey(); if (key != null) { // Convert key to name. String name = key.substring(<prefix>.length()); connectionComboBox.setSelectedItem(name); } } private KeyValueStoreServices getKeyValueStoreUIServices() { return (KeyValueStoreServices) consoleContext.get(KeyValueStoreServices.CONSOLE_CONTEXT_KEY); } }
The following sample code illustrates how the
KeyValueStoreServices
features might be used in Custom Assertion Task Action Dialog class:public class ManageSalesForceConnectionsDialog extends JDialog { private Map consoleContext; public ManageSalesForceConnectionsDialog(Frame owner, Map consoleContext) { super(owner, "Manage Salesforce Connections", true); this.consoleContext = consoleContext; this.initialize(); ... } private void initialize() { // Initalize UI components. ... } private KeyValueStore getKeyValueStore() { KeyValueStoreServices keyValueStoreServices = (KeyValueStoreServices)consoleContext.get(KeyValueStoreServices.CONSOLE_CONTEXT_KEY); return keyValueStoreServices.getKeyValueStore(); // Get the default key value store. } private void onAdd() { KeyValueStore keyValueStore = this.getKeyValueStore(); SalesForceConnection connection = new SalesForceConnection(); // Populate fields in connection. ... // Convert connection name to a key. // The name is what is displayed in the UI. // The key is what is stored in the custom key value store. // The key should contain a prefix that is unique to your custom assertion. For example, the class name. String key = <prefix>+<name>; // Encode Java object to byte[]. byte[] value = SalesForceConnectionUtils.toBytes(connection); keyValueStore.save(key, value); } private void onEdit() { KeyValueStore keyValueStore = this.getKeyValueStore(); SalesForceConnection connection = new SalesForceConnection(); // Populate fields in connection. ... // Convert connection name to a key. String key = <prefix>+<name>; // Encode Java object to byte[]. byte[] value = SalesForceConnectionUtils.toBytes(connection); keyValueStore.update(key, value); } private void onRemove() { KeyValueStore keyValueStore = this.getKeyValueStore(); // Convert connection name to a key. String key = <prefix>+<name>; keyValueStore.delete(key); } private void reloadConnectionTable() { KeyValueStore keyValueStore = this.getKeyValueStore(); Map<String, byte[]> map = keyValueStore.findAllWithKeyPrefix(<prefix>); for (Map.Entry<String, byte[]> entry : map.entrySet()) { byte[] value = entry.getValue(); // Decode byte[] to Java object. SalesForceConnection connection = SalesForceConnectionUtils.fromBytes(value); // Convert key to connection name. String key = entry.getKey(); String name = key.substring(<prefix>.length()); ... } } } }
The following sample code illustrates how the
KeyValueStoreServices
features might be used in CustomExtensionInterfaceBinding
class:public class SalesForceCustomExtensionInterfaceBinding extends CustomExtensionInterfaceBinding { public SalesForceCustomExtensionInterfaceBinding() { super(SalesForceCustomExtensionInterface.class, new SalesForceCustomExtensionInterface() { @Override public String[] describeGlobal(String connectionKey) throws Exception { ServiceFinder serviceFinder = ...; // obtain from customPolicyContext KeyValueStoreServices keyValueStoreServices = serviceFinder.lookupService(KeyValueStoreServices.class); KeyValueStore keyValueStore = keyValueStoreServices.getKeyValueStore(); // Get the default key value store. byte[] value = keyValueStore.get(connectionKey); if (value == null) { throw new Exception("Cannot find SalesForce Connection with the specified key: " + connectionKey); } // Decode byte[] to Java object. SalesForceConnection connection = SalesForceConnectionUtils.fromBytes(value); String username = connection.getUsername(); String passwordId = connection.getPasswordId(); String securityTokenId = connection.getSecurityTokenId() ... } }); } }
The following sample code illustrates how the
KeyValueStoreServices
features might be used in ServiceInvocation
class:public class SalesForceConnectorServiceInvocation extends ServiceInvocation { @Override public CustomAssertionStatus checkRequest(final CustomPolicyContext customPolicyContext) { ServiceFinder serviceFinder = ...; // obtain from customPolicyContext KeyValueStoreServices keyValueStoreServices = serviceFinder.lookupService(KeyValueStoreServices.class); KeyValueStore keyValueStore = keyValueStoreServices.getKeyValueStore(); // Get the default key value store. byte[] value = keyValueStore.get(sfdcAssertion.getConnectionKey()); if (value == null) { return CustomAssertionStatus.FAILED; } // Decode byte[] to Java object. SalesForceConnection connection = SalesForceConnectionUtils.fromBytes(value); String username = connection.getUsername(); String passwordId = connection.getPasswordId(); String securityTokenId = connection.getSecurityTokenId(); ... } }
Change Event Notification
The custom key value store supports change event notification. This allows for registration of callbacks that get triggered when a key value is modified in the custom key value store. In the Salesforce Sample, this is used to get notification when a Salesforce Connection is deleted or its properties (that is, description, username, password ID, security token ID, enabled) modified. Because the Salesforce Sample caches Salesforce Connections, it can be notified to clear the cache if a Salesforce Connection has been deleted or modified.
The following sample code shows how to implement the
KeyValueStoreChangeEventListener.Callback
interface to invalidate the Salesforce Connection cache.public class SalesForceKeyValueStoreChangeEventCallback implements KeyValueStoreChangeEventListener.Callback { private static SalesForceKeyValueStoreChangeEventCallback instance = null; private final SalesForceClientCacheManager salesForceClientCacheManager; public static SalesForceKeyValueStoreChangeEventCallback getInstance(SalesForceClientCacheManager salesForceClientCacheManager) { if (instance == null) { instance = new SalesForceKeyValueStoreChangeEventCallback(salesForceClientCacheManager); } return instance; } private SalesForceKeyValueStoreChangeEventCallback(SalesForceClientCacheManager salesForceClientCacheManager) { this.salesForceClientCacheManager = salesForceClientCacheManager; } @Override public String getKeyPrefix() { return SalesForceConnectionUtils.SALESFORCE_CONNECTION_NAME_PREFIX; } @Override public void onEvent(List<KeyValueStoreChangeEventListener.Event> events) { for (KeyValueStoreChangeEventListener.Event event : events) { // Ignore Create operation, since it won't be in the cache. if (KeyValueStoreChangeEventListener.Operation.UPDATE == event.getOperation() || KeyValueStoreChangeEventListener.Operation.DELETE == event.getOperation()) { // Invalidate connection key in the cache. // salesForceClientCacheManager.invalidate(event.getKey()); } } } }
The following sample code shows how to register and unregister the key value store change callback:
public class SalesForceConnectorCustomAssertion implements CustomAssertion, CustomDynamicLoader { ... @Override public void onLoad(final ServiceFinder serviceFinder) throws CustomLoaderException { ... final SecurePasswordServices securePasswordServices = serviceFinder.lookupService(SecurePasswordServices.class); final KeyValueStoreServices keyValueStoreServices = serviceFinder.lookupService(KeyValueStoreServices.class); final KeyValueStore keyValueStore = keyValueStoreServices.getKeyValueStore(); // create the salesforce cache manager singleton instance final SalesForceClientCacheManager salesForceClientCacheManager = SalesForceClientCacheManager.getInstance(Config.getInstance(), securePasswordServices); // register for key value store change event listener. KeyValueStoreChangeEventListener listener = keyValueStore.getListener(KeyValueStoreChangeEventListener.class); if (listener != null) { listener.add(SalesForceKeyValueStoreChangeEventCallback.getInstance(salesForceClientCacheManager)); } ... } @Override public void onUnload(final ServiceFinder serviceFinder) { ... final SecurePasswordServices securePasswordServices = serviceFinder.lookupService(SecurePasswordServices.class); final KeyValueStoreServices keyValueStoreServices = serviceFinder.lookupService(KeyValueStoreServices.class); final KeyValueStore keyValueStore = keyValueStoreServices.getKeyValueStore(); // unregister for key value store change event listener. KeyValueStoreChangeEventListener listener = keyValueStore.getListener(KeyValueStoreChangeEventListener.class); if (listener != null) { listener.remove(SalesForceKeyValueStoreChangeEventCallback.getInstance(salesForceClientCacheManager)); } ... } ... }