Set a JMter variable with an groovy collection (JSR223 PostProcessor) - groovy

I'm trying to set a variable in JMter with the value in a List that I have in JSR223 Processor (Groovy). For that, I'm using the method vars.putObject, but when I try to use this variable in a ForEach Controller the loop doesn't execute.
My PostProcessor has the following flow:
Get a list of strings that were generated by a Regular Expression Extractor
Create a List with the valid values for the test (filter some values)
Add the result in a JMter variable vars.putObject
import org.apache.jmeter.services.FileServer
int requestAssetsCount = vars.get("CatalogAssetIds_matchNr").toInteger()
int maxAssetsNumbers = vars.get("NumberAssets").toInteger()
List<String> validAssets = new ArrayList<String>()
def assetsBlackListCsv = FileServer.getFileServer().getBaseDir() + "\\\assets-blacklist.csv"
File assetsBlackListFile = new File(assetsBlackListCsv)
List<String> assetsBlackList = new ArrayList<String>()
log.info("Loading assets black list. File: ${assetsBlackListCsv}")
if (assetsBlackListFile.exists()) {
assetsBlackListFile.eachLine { line ->
assetsBlackList.add(line)
}
}
else {
log.info("Black list file doesn't exist. File: ${assetsBlackListCsv}")
}
log.info("Verifying valid assets")
for (def i = 1; i < requestAssetsCount; i++) {
def assetId = vars.get("CatalogAssetIds_${i}_g1")
if (!assetsBlackList.contains(assetId)) {
validAssets.add(assetId)
}
else {
log.info("Found a blacklisted asset. Skipping it. Asset ID: ${assetId}")
}
if (validAssets.size() >= maxAssetsNumbers) {
break
}
}
I've tried (like regular extractor):
log.info("Storing valid assets list")
vars.putObject("ValidCatalogAssetIds_matchNr",validAssets.size())
for(def i = 0; i < validAssets.size(); i++) {
vars.putObject("ValidAssetIds_${i+1}_g",1)
vars.putObject("ValidAssetIds_${i+1}_g0","\"id\":\"${validAssets[i]}\"")
vars.putObject("ValidAssetIds_${i+1}_g1",validAssets[i])
}
I've tried (set list value):
log.info("Storing valid assets list")
vars.putObject("ValidAssetIds",validAssets)

Concat strings as "+ (i+1) + "
vars.putObject("ValidCatalogAssetIds_"+ (i+1) + "_g",1)
vars.putObject("ValidAssetIds_"+ (i+1) + "_g0","\"id\":\"${validAssets[i]}\"")
vars.putObject("ValiAssetIds_"+ (i+1) + "_g1",validAssets[i])
Don't use ${} syntax in JSR223 scripts because it will initialize values before script executed and not as expected

Related

Groovy extract list of XML nodes in another file

I have xml file which contains thousands of xml nodes. In my groovy code I want to extract N of them and push into another file. How I can do this with groovy?
Some sample listing:
def ibr = new File('/Users/alex/Downloads/temp.xml');
def ibrParser = new XmlParser().parseText(ibr.getText());
def groups = [];
int current = 0;
ibrParser.each { group ->
if (current <= 100) {
groups<<group
}
current++
}
//how to store groups as xml into another file?
Following code helped me:
int current = 0;
def nodeToSerialize;
ibrParser.each { renewalQuote ->
if (current <= 100) {
if (nodeToSerialize == null) {
nodeToSerialize = renewalQuote;
} else {
nodeToSerialize.append(renewalQuote)
}
}
current++
}
XmlUtil.serialize(nodeToSerialize, new FileOutputStream("/Users/alex/Downloads/100Cases.xml"))

How to compose variable name dynamically?

I need to generate a list,and name it's items based on for-loop index
number, like this:
for(int i=0;i<someNumber;i++){
Model m_{$i}=Mock() //but this doesn't work
......
models.add(i,m_{$i})
}
then they can be distinguished by name when debugging test code(shame to tell this) within eclipse,but it doesn't work, so how to make it work?
update:add image to tell why I want to append for-loop index to variable name
You can also add some property to your Mock class at runtime thanks to Groovy's MetaClass. Take a look at this sample snippet:
class myClass {
String someProperty
}
def models = []
10.times { it ->
def instance = new myClass(someProperty: "something")
instance.metaClass.testId = it
models.add(instance)
}
// delete some
println "Removing object with testId = " + models.remove(4).testId
println "Removing object with testId = " + models.remove(7).testId
def identifiersOfObjectsAfterRemoves = models.collect { it.testId }
def removedObjectsIdentifiers = (0..9) - identifiersOfObjectsAfterRemoves
println "Identifiers of removed objects: " + removedObjectsIdentifiers

Efficient way to implement excel import in grails

This code should probably go in code review but I won't get quick response there (Only 2 groovy questions there).
I have the following code for importing data from excel into my grails application. The problem is that I didn't test for >1000 rows in the excel file so my app froze when my client tried to upload 13k rows. I have checked the stacktrace.log (app is in production) but no exception. The system admin thinks the jvm ran out of memory. We have increased the size of the heap memory. However, I want to ask if there's a better way to implement this. I am using apache poi and creating domain objects as I read each row from excel. After that, I pass the list of objects to the controller that validates and saves them in the database. Should I tell my client to limit number of items imported at a time? Is there a better way to write this code?
def importData(file, user){
def rows = []
def keywords = Keyword.list()
int inventoryCount = Inventory.findAllByUser(user).size()
def inventory = new Inventory(name:"Inventory ${inventoryCount +1}", user:user)
Workbook workbook = WorkbookFactory.create(file)
Sheet sheet = workbook.getSheetAt(0)
int rowStart = 1;
int rowEnd = sheet.getLastRowNum() + 1 ;
for (int rowNum = rowStart; rowNum < rowEnd; rowNum++) {
Row r = sheet.getRow(rowNum);
if(r != null && r?.getCell(0, Row.RETURN_BLANK_AS_NULL)!=null ){
def rowData =[:]
int lastColumn = 8;
for (int cn = 0; cn < lastColumn; cn++) {
Cell c = r.getCell(cn, Row.RETURN_BLANK_AS_NULL);
if (c == null) {
return new ExcelFormatException("Empty cell not allowed",rowNum+1, cn+1)
} else {
def field = properties[cn+1]
if(field.type==c.getCellType()){
if(c.getCellType()==text){
rowData<<[(field.name):c.getStringCellValue().toString()]
}else if(c.getCellType()==numeric){
if(field.name.equalsIgnoreCase("price") ){
rowData<<[(field.name):c.getNumericCellValue().toDouble()]
}else{
rowData<<[(field.name):c.getNumericCellValue().toInteger()]
}
}
}else{
return new ExcelFormatException("Invalid value found", rowNum+1, cn+1)
}
}
}
def item = new InventoryItem(rowData)
String keyword = retrieveKeyword(item.description, keywords)
String criticality = keyword?"Critical":"Not known"
int proposedMin = getProposedMin(item.usagePerYear)
int proposedMax = getProposedMax(proposedMin, item.price, item.usagePerYear, item?.currentMin)
String inventoryLevel = getInventoryLevel(item.usagePerYear, item.quantity, proposedMin, item.currentMin)
item.proposedMin = proposedMin
item.proposedMax = proposedMax
item.inventoryLevel = inventoryLevel
item.keyword = keyword
item.criticality = criticality
inventory.addToItems(item)
}
}
return inventory
}
Functions used in above code:
def retrieveKeyword(desc, keywords){
def keyword
for (key in keywords){
if(desc.toLowerCase().contains(key.name.toLowerCase())){
keyword = key.name
break
}
}
return keyword
}
int getProposedMin(int usage){
(int) ((((usage/12)/30) *7) + 1)
}
int getProposedMax(int pmin, double price, int usage, int cmin){
int c = price == 0? 1: ((Math.sqrt((24 * (usage/12)*5)/(0.15*price))) + (pmin - 1))
if(cmin >= c){
return pmin
}
return c
}
String getInventoryLevel(int usage, int qty, int proposedMin, int currentMin){
if(qty != 0){
double c = usage/qty
if(usage==0)
return "Excess"
if(c<0.75){
return "Inactive"
}else if(proposedMin<currentMin){
return "Excess"
}else if(c>=0.75){
return "Active"
}
}else if(usage==0 && qty == 0){
return "Not used"
}else if(usage>3 && qty ==0){
return "Insufficient"
}else if(proposedMin > currentMin){
return "Insufficient"
}
}
Controller action:
def importData(){
if(request.post){
def file = request.getFile("excelFile")
//validate file
def file_types = ["application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
if(!file_types.contains(file.getContentType())){
render view:"importData", model:[error:"Invalid File type"]
return
}
def inv = excelService.importData(file.getInputStream(),User.get(principal.id))
if(inv){
if(inv instanceof ExcelFormatException){
def err = (ExcelFormatException) inv
render view:"importData", model:[error:err.message +". Error occurred at: Row: "+err.row+" Col: "+err.col]
return
}else{
render view:"viewData", model:[inventory:inv]
return
}
}
}
}
Hibernate and GORM require some tuning when dealing with bulk imports. Two suggestions:
Follow the techniques found here: http://naleid.com/blog/2009/10/01/batch-import-performance-with-grails-and-mysql (written with MySQL in mind, but the concepts are pertinent to any RDBMS)
Don't use a collection to map the relationship between Inventory and InventoryItem. Remove the items collection from Inventory and instead add an Inventory field to your InventoryItem class. Burt Beckwith covers this in great detail here: http://burtbeckwith.com/blog/?p=1029
Using a plugin would be a better option.
I use this plugin - http://grails.org/plugin/excel-import

groovy.lang.MissingMethodException: No signature of method

I am getting the following error -
groovy.lang.MissingMethodException: No signature of method: Script64$_run_closure5_closure7_closure8_closure9_closure10_closure11.doCall() is applicable for argument types: (java.lang.String) values: Possible solutions: doCall(java.lang.Object, java.lang.Object), isCase(java.lang.Object), isCase(java.lang.Object) error at line:
Code - EDIT
import groovy.xml.*
List tempList = []
List listgenerated = []
def count = 0
for (a in 0..totalCount-1)
{
//nameList and valueList lists will have all the contents added as below commented pseudo code
/*for (b in 0..50)
{
nameList.add(b,number) // number is some calculated value
valueList.add(b,number)
e.g. nameList=[name1, name2, name3,name4, name5]
valueList =[val1, val2, val3, , val5]
listgenerated should be = [[name1:val1, name2:val2], [name3:val3, name4: , name5:val5]]
} */
tempList = []
for (j in count..nameList.size())
{
count = j
def nameKey = nameList[j]
def value
if (nameKey != null)
{
value = valueList[j]
tempList << [(nameKey) : value]
}
}
count = count
listgenerated.putAt(a,tempList)
number = number +1
}
def process = { binding, element, name ->
if( element[ name ] instanceof Collection ) {
element[ name ].each { n ->
binding."$name"( n )
}
}
else if( element[ name ] ) {
binding."$name"( element[ name ] )
}
}
class Form {
List fields
}
def list = [[ name:'a', val:'1' ], [ name:'b', val :'2', name2:4, xyz:'abc', pqr:'']] //Edited list
f = new Form( fields: list ) //Works fine
f = new Form( fields: listgenerated ) //Gives the above error
String xml = XmlUtil.serialize( new StreamingMarkupBuilder().with { builder ->
builder.bind { binding ->
data {
f.fields.each { fields ->
item {
fields.each { name, value ->
process( binding, fields, name )
}
}
}
}
}
} )
If while creating the "listgenerated" single quotes are added around values it takes it as character and while printing both lists seem different.
I am unable to figure it out what exactly is going wrong. Any help is appreciated. Thanks.
Ref - Groovy: dynamically create XML for collection of objects with collections of properties
I believe, where you do:
//some loop to add multiple values to the list
listgenerated << name+":"+value
You need to do:
//some loop to add multiple values to the list
listgenerated << [ (name): value ]
And add a map to the list rather than a String. It's hard to say though as your code example doesn't run without alteration, and I don't know if it's the alterations that are solving the problem

Groovy, collect objects following a property while not null?

Is there already a way in groovy to collect objects following a property while not null ?
Object.metaClass {
collectWhileNotNull = { Closure follow ->
def result = []
def previous = null
for (def current = delegate; !current.is(previous) && (current != null); current = follow(current)){
result << current
previous = current
}
return result
}
}
It is useful for recursive data structure.
An example of usage for a groovy.util.Node :
Closure getAncestors = { Node n -> n.collectWhileNotNull{ it.parent() }.tail().reverse() }
You can use a Generator class (this is also the name of the necessary pattern) from cookbook.

Resources