Rick.Brown

Advanced ParameterList Methods

Blog Post created by Rick.Brown Employee on Feb 15, 2017

There are times when I need to change the request arguments en-masse in a service image generation. In historic and current versions of CA SV (DevTest), the arguments are stored in what is called a “ParameterList”, and each argument is a “Parameter” inside that ParameterList.

 

The Scriptable Data Protocol Handler (DPH) is what I use for manipulating these items. DevTest provides the ability to code DPHs, but I try not to use this facility, because it needs re-compiling whenever a DevTest API changes, it cannot be owned or changed by the people who will use it, it complains that “ParameterList” is deprecated, and my coding isn’t good enough to produce anything that I would be comfortable providing.

 

The scriptable DPH provides the following comment when it’s added to a recording:

>/*

>// You can use %beanshell%, %groovy% or %javascript% or some other installed JSR-223 scripting language

>// This example is for beanshell

>import com.itko.util.ParameterList;

>// Manipulate operation

>String operation = lisa_vse_request.getOperation();

>lisa_vse_request.setOperation(operation + " - updated");

>// This is implicitly set by calling setBodyText() or setBodyBytes

>boolean isBinary = lisa_vse_request.isBinary();

>lisa_vse_request.setBinary(false);

>// Manipulate request body text

>String theBody = lisa_vse_request.getBodyText();

>lisa_vse_request.setBodyText("New body");

>// Manipulate request body as binary

>byte[] b = lisa_vse_request.getBodyBytes();

>lisa_vse_request.setBodyBytes(b);

>// Other

>String asString = lisa_vse_request.toString();

>long id = lisa_vse_request.getId();

>// Arguments, Attributes, and Metadata are all ParameterList

>ParameterList args = lisa_vse_request.getArguments();

>lisa_vse_request.setArguments(args);

>ParameterList attributes = lisa_vse_request.getAttributes();

>lisa_vse_request.setAttributes(attributes);

>ParameterList metadata = lisa_vse_request.getMetaData();

>lisa_vse_request.setMetaData(metadata);

>// Working with ParameterList

>ParameterList p = new ParameterList();

>// Do we want to allow dupes or not?

>p.setAllowDupes(true);

>boolean areDupesAllowed = p.isDupesAllowed();

>// Adding parameters

>p.addParameters("key1=val1&key2=val2");  // many at once

>p.addParameter(new Parameter("key3", "val3")); // one at a time

>// Looking up parameters

>String theVal = p.getParameterValue("key1");

>// Updating parameters

>p.setParameterValue("key3", "newVal");

>// Removing parameters

>p.removeParameter("key1");

>// Removing all parameters

>p.clear();

>*/

This is fine as far as it goes, and it hints that there might be other possibilities, but what if I need to do any more advanced things? This week, I had the need to parse-and-change some request blocks. I had parameters called:

Root_path_featureType

Root_path_featureCode

Root_path_optionalFlag

Unfortunately, the request message had a variable number of these blocks, so I ended up with the following (dummy data used):

Root_path_featureType_1    =                “provider”

Root_path_featureCode_1   =                “DevTest”

Root_path_optionalFlag_1   =                “O”

Root_path_featureType_2    =                “version”

Root_path_featureCode_2   =                “10.0.0”

Root_path_optionalFlag_2   =                “O”

etc.

 

I determined that the actual data that a virtual service would care about was featureCode, and the other parameters were simply markers for the application to separate each featureCode. DevTest can’t understand the implications of this this out-of-the-box, so its behavior might not be what’s expected. In my case, what I got was this:

Response_path_provider      =                {{=request_Root_path_featureCode_1/*“DevTest”*/}}

Response_path_version        =                {{=request_Root_path_featureCode_2/*“10.0.0”*/}}

Unfortunately, the application sent the request in random orders, so I occasionally responded with:

Response_path_provider      =                “10.0.0”

Response_path_version        =                “DevTest”

DevTest is unable to do “=request_Root_path_featureCode WHERE request_Root_path_featureType = ‘provider’ “, because there’s no database-like indexing in messages, so I need to give it some help. I reckon DevTest would do best if it stored the parameters as:

Root_path_featureCode_{{featureType}}{{optionalFlag}}

For the above, it would translate to:

Root_path_featureCode_providerO   =   "DevTest"

Root_path_featureCode_versionO     =   "10.0.0"

Much better! Easier to read, easier to understand and decreasing the number of parameters by two thirds!

 

In this way, DevTest is far less likely to hit matching inconsistencies for featureCode, unless featureType and optionalFlag are both identical (in which case, the featureCode values won’t need to be in any specific order, so it’s likely to work anyway).

 

So, what I need to do is to read all the parameters, search for the strings “featureCode”, “featureType” and “optionalFlag”, and if I find one of them, work out how to replace, remove and add parts. What I need it to be able to iterate through the ParameterList like it’s a Java Map, and it’s very similar to a Map, but not similar enough. What I therefore need are some extra methods I can use for ParameterList to make it more flexible.

 

Fortunately, extra methods exist – they just aren’t documented anywhere I can find them!

 

To see what methods are available, you could load the class in an IDE, but that’s too much like developer work for me – what I want is something in DevTest Workstation that’ll let me see what’s happening.

  • In a test, add a step. The step wants to be a Dynamic Java Execution step.
  • You want to “Make new object of class:” com.itko.util.UniqueKeysParameterList
  • Construct/Load Object…
  • Constructor: UniqueKeysParameterList( )
  • Finish
  • Highlight the root element in the Object Call Tree, and you see the “Data Sheet” tab filled
  • Click on the Call Sheet tab

Ok, here are all the methods we can use against ParameterLists! Handy!

Here’s the complete list:

Boolean

containsKey( java.lang.String key )

Boolean

equals( java.lang.Object o )

Boolean

hasDupeKey( boolean ignoreCase )

Boolean

hasEmptyKey( )

Boolean

hasMatchingParameter( com.itko.util.Parameter p )

Boolean

isDupesAllowed( )

Boolean

isEncoded( )

com.itko.util.Parameter

get( int index )

com.itko.util.Parameter

getMatchingParameter( com.itko.util.Parameter p )

com.itko.util.Parameter

getParameter( java.lang.String key )

com.itko.util.Parameter

getParameterByValue( java.lang.String value )

com.itko.util.Parameter

getTerm( java.lang.String key )

com.itko.util.ParameterList

buildTempUnmergedList( )

com.itko.util.ParameterList

cloneQuick( )

Integer

capacity( )

Integer

hashCode( )

Integer

size( )

Integer

totalWeight( )

Class

getClass( )

Object

clone( )

String

get( java.lang.String key )

String

getDelimiter( )

String

getMergeDupesDelimiter( )

String

getParameterValue( java.lang.String key )

String

getTermValue( java.lang.String key )

String

toArgumentString( )

String

toAttributeString( )

String

toDecodedArgumentString( )

String

toEncodedArgumentString( )

String

toString( )

String

writeXMLString( int indent )

String[]

getKeyArray( )

String[]

getValueArray( )

String[]

unMergeParameterValues( com.itko.util.Parameter p )

java.util.Enumeration

terms( )

java.util.Iterator

iterator( )

java.util.Map

getAllKeyValuePairs( )

java.util.Spliterator

spliterator( )

Void

addAll( com.itko.util.ParameterList arg1 )

Void

addAll( com.itko.util.ParameterList newOnes, boolean allowDupes )

Void

addAll( java.util.Map newOnes )

Void

addChildNodes( org.w3c.dom.Element parent )

Void

addNodeAttributes( org.w3c.dom.Element el )

Void

addParameter( com.itko.util.Parameter arg1 )

Void

addParameter( int arg1, com.itko.util.Parameter arg2 )

Void

addParameters( java.lang.String paramData )

Void

addParameters( java.lang.String paramData, boolean encoded )

Void

clear( )

Void

decodeParamsFromUnicode( )

Void

encodeParamsToUnicode( )

Void

forEach( java.util.function.Consumer arg1 )

Void

initialize( org.w3c.dom.Element parent )

Void

moveDown( int row )

Void

moveUp( int row )

Void

put( java.lang.String key, java.lang.String value )

Void

readEncryptedPropertyFile( java.lang.String fname )

Void

readPropertyFile( java.lang.String fname )

Void

readPropertyStream( java.io.InputStream inputStream )

Void

remove( int index )

Void

removeAllParameters( )

Void

removeParameter( java.lang.String key )

Void

removeParamsWithEmptyKey( )

Void

removeTerm( java.lang.String key )

Void

reset( )

Void

setAllowDupes( boolean allowDupes )

Void

setDelimiter( java.lang.String delimiter )

Void

setEncoded( boolean encoded )

Void

setMergeDupesDelimiter( java.lang.String mergeDupesDelimiter )

Void

setParameterValue( java.lang.String key, java.lang.String value )

Void

sortOnKeys( )

Void

sortOnValues( )

Void

writeEncryptedPropsFile( java.io.OutputStream os )

Void

writeEncryptedPropsFile( java.io.PrintWriter ps )

Void

writePropertyFile( java.io.PrintWriter ps )

Void

writeSimpleUnsafeXML( java.io.PrintWriter pw, int indent )

Void

writeXML( java.io.PrintWriter pw, int indent )

Void

writeXML( java.io.PrintWriter pw, int indent, boolean writeTypeMap )

Void

writeXML( java.io.PrintWriter pw, int indent, boolean writeTypeMap, boolean usesUnicode )

I can do just about anything I can think of, using all these methods! So how can I leverage this stuff for what I need to do in this use case? I need to import ParameterList (and Parameter, as I will be adjusting what’s inside the ParameterList), and I want to create a map through which I can iterate, searching for the strings for the data I want to combine, and then remove the matched parameters and add the new parameters. Here’s how I did it:

import com.itko.util.ParameterList;

import com.itko.util.Parameter;

ParameterList args = lisa_vse_request.getArguments();

Map argMap = args.getAllKeyValuePairs();

int arrayLength = 200;

String[][] argArray = new String[arrayLength][4];

for (String key : argMap.keySet()) {

    if(key.contains("FeatureType")) {

        int lineNo = Integer.parseInt(key.substring(key.lastIndexOf("_") + 1));

        argArray[lineNo][0] = args.getParameterValue(key);

        args.removeParameter(key);

        _logger.debug("Key = {}, {}, {}", key, argArray[lineNo][0]);

    } else if(key.contains("FeatureCode")) {

        int lineNo = Integer.parseInt(key.substring(key.lastIndexOf("_") + 1));

        argArray[lineNo][1] = args.getParameterValue(key);

        argArray[lineNo][3] = key.substring(0, key.lastIndexOf("_"));

        args.removeParameter(key);

        _logger.debug("Key = {}, {}", key, argArray[lineNo][1], argArray[lineNo][3]);

    } else if(key.contains("OptionalFlag")) {

        int lineNo = Integer.parseInt(key.substring(key.lastIndexOf("_") + 1));

        argArray[lineNo][2] = args.getParameterValue(key);

        args.removeParameter(key);

        _logger.debug("Key = {}, {}", key, argArray[lineNo][2]);

    }

}

 

for(counter = 1;counter < arrayLength;counter++) {

    if(argArray[counter][0] != null)

        args.addParameter(new Parameter(argArray[counter][3] + "_" + argArray[counter][0] + argArray[counter][2], argArray[counter][1]));

}

Are there more elegant ways of doing this? Undoubtedly (the simplest might be to use argMap.valueSet instead of args.getParameterValue)! But this is the way that I could easily understand and that I could quickly script! If there comes a time when I need improved performance or debug any non-functional issues, I can look at using different Java.

Outcomes