How to replace backward slash (\) to forward slash (/) using jmeter BeanShell - java

I have declared a user defined variable as 'projectHome' using below BeanShell which returns the absolute path of the jmx file.
${__BeanShell(import org.apache.jmeter.services.FileServer; FileServer.getFileServer().getBaseDir();)}
On Windows it returns: projectHome=C:\Users\dd\Desktop\API_Testing
On MacOS it returns:
projectHome=/Users/dd/Desktop/API_Testing
The variable value is working fine in following BeanShell Sampler on MacOS:
import java.io.FileWriter;
import java.util.Arrays;
import java.io.Writer;
import java.util.List;
//Default separator
char SEPARATOR = ',';
//function write line in csv
public void writeLine(FileWriter writer, String[] params, char separator) {
boolean firstParam = true;
StringBuilder stringBuilder = new StringBuilder();
String param = "";
for (int i = 0; i < params.length; i++) {
//get param
param = params[i];
log.info(param);
//if the first param in the line, separator is not needed
if (!firstParam) {
stringBuilder.append(separator);
}
//Add param to line
stringBuilder.append(param);
firstParam = false;
}
//prepare file to next line
stringBuilder.append("\n");
//add to file the line
log.info(stringBuilder.toString());
writer.append(stringBuilder.toString());
}
//get path of csv file (creates new one if its not exists)
String csvFile = "${projectHome}/tenant_details.csv";
String[] params = {"${Email}"};
FileWriter fileWriter = new FileWriter(csvFile, true);
writeLine(fileWriter, params, SEPARATOR);
//proper close to file
fileWriter.flush();
fileWriter.close();
When I am running the script on Windows machine, it is failing because of the path having backslash.
How can I convert the backslash to forward slash within the same BeanShell Sampler so that it can work on Windows as well as MacOS?

You don't need to replace slashes / with backslashes \, slashes will work file for Windows platform as well, you can do something like:
new File("c:/Windows/system32/cmd.exe")
and it will normally resolve.
Don't refer variables like ${projectHome} and/or ${Email} in scripts, use vars shorthand instead. It stands for JMeterVariables class instance and provides read/write access to all JMeter Variables in scope. So you should be using
String csvFile = vars.get("projectHome") + "/tenant_details.csv";
and
String[] params = {vars.get("Email")};
Since JMeter 3.1 it is recommended to use JSR223 Test Elements and Groovy language for scripting so consider migrating to Groovy on next available opportunity as Beanshell (and will) become a performance bottleneck when it comes to high loads. See Apache Groovy - Why and How You Should Use It article for more information.

In beanshell Sampler/Post-PreProcessor, input the path to script folder and script name into the Script File field as:-
${__BeanShell(File.separator,)} will return the / in Unix (Mac, Linux, …) and \ in Windows. So you can consider it as:
– Windows: ${PATH}\Foldername\beanshellfile.bsh
– Unix: ${PATH}/scripts/beanshellfile.bsh

Inside Sampler you can call File.separator which uses relevant OS file separator when you need to add it. In your code change to:
String csvFile = "${projectHome}" + File.separator + "tenant_details.csv";

Here is how I solved this problem using BeanShell:
import org.apache.jmeter.services.FileServer;
String JMXPath = FileServer.getFileServer().getBaseDir();
char[]a = JMXPath.toCharArray();
log.info(JMXPath + " - It is project JMX path");
for (int i = 0; i < a.length; i++)
{
if (a[i] == '\\')
{
a[i] = '/';
}
}
String xyz = String.valueOf(a);
log.info(xyz + " - It is projectHome value");
vars.put("projectHome", xyz);

Related

Correct way to trim a file name to use as a string?

I am making a javaFX project using data from files in a data directory as input. I want to use each file name as a String variable. I have already loaded the data:
File path = new File("data");
File [] files = path.listFiles();
for (int i = 0; i < files.length; i++){
if (files[i].isFile()){
items.loadData(files[i].toString());
}
The toString is now data/fileName1.csv. I want to save use fileName1 only. I have tried using the split() method in the loadData() method, like this:
fileName= fileName.split("/")[1]; //should trim to fileName1.csv, works
fileName= fileName.split("\.")[0]; //should trim to fileName1, error
The error for the second split() is "invalid escape sequence (valid ones are \b \t \n \f \r \" \' \ )".
I have tried variations to fix this. Is there a better/more efficient way to accomplish this?
Edit: Changed trim to split
You need to escape your backslash. Try \\.
But for operations like this, you should probably use Path functions such as: https://docs.oracle.com/javase/7/docs/api/java/nio/file/Path.html#getFileName()
You could use the following code and get the filename in just one line.
files[i].substring(files[i].indexOf("/") + 1);
You could do the following :
String fileName = "data/fileName1.csv";
String [] arr = fileName.split("(/|\\.)");
fileName = arr[1];

Beansheel sampler is not stopping after execution

I am printing the responses in a csv file using beanshell sampler but it is not stopping after completion.
What can be done so that it stops after printing it. Below is the sample code I have used acctId is used in the pre processor from other thread group.
import java.io.FileWriter;
import java.util.Arrays;
import java.io.Writer;
import java.util.List;
char SEPARATOR = ',';
public void writeLine(FileWriter writer, String[] params, char separator)
{
boolean firstParam = true;
StringBuilder stringBuilder = new StringBuilder();
String param = "";
for (int i = 0; i <params.length; i++)
{
param = params[i];
log.info(param);
if (!firstParam)
{
stringBuilder.append(separator);
}
stringBuilder.append(param);
firstParam = false;
}
stringBuilder.append("\n");
log.info(stringBuilder.toString());
writer.append(stringBuilder.toString());
}
String csvFile = "D:/jmeter/test1/result.csv"; // for example '/User/Downloads/blabla.csv'
//String[] params = {"${acctId}", "${tranId}"};
String[] params = {"${acctId}"};
FileWriter fileWriter = new FileWriter(csvFile, true);
writeLine(fileWriter, params, SEPARATOR);
fileWriter.flush();
fileWriter.close();
You can use "View Result Tree" Sampler or "Simple Data Writer" to save the response messages. Just click "Configure" and use save as XML and select "Save response data(XML)" with other required fields. Thought, it is not recommended for load test.
The recommended way of saving a variable into a CSV file is using Sample Variables property
Add the next line to user.properties file (lives in "bin" folder of your JMeter installation)
sample_variables=acctId
Restart JMeter to pick the property up
That's it now when you run your JMeter test in command-line non-GUI mode like:
jmeter -n -t test.jmx -l result.jtl
You will see an extra column in the result.jtl file holding the value of the acctId variable for each sampler.
Also be aware that starting from JMeter 3.1 it is recommended to use Groovy for any form of scripting. You will be able to replace your code with something like:
new File('D:/jmeter/test1/result.csv') << vars.get('acctId') << System.getProperty('line.separator')
If you don't like Groovy syntax be aware that you can use FileUtils.writeStringToFile() function

Filepath in Javascript

I am setting a file path in java as follows,
String requestedFileName = param+File.separator+fileName;
request.setAttribute("requestedFileName",requestedFileName);
and i am retreving it in javascript as follows,
var selectedFileName = null;
if ('${requestScope.requestButtonParam}' == "convert") {
selectedFileName = '${requestScope.requestedFileName}';
alert("inside if: "+selectedFileName);
}
But in the alert box,i am getting file path as below
D:NewFolderAbc.html instead of D://NewFolder//Abc.html
How to get the filepath correctly in javascript?
Thanks
The trouble is most likely that your File.separator is \ which is an escape character in Javascript, so you're going to have to escape the escape character.
An easy way of doing the escaping is using Apache Commons StringEscapeUtils.escapeEcmaScript:
String requestedFileName = param+File.separator+fileName;
String escapedFileName = StringEscapeUtils.escapeEcmaScript(requestedFileName);
request.setAttribute("requestedFileName",escapedFileName);
This will change D:\NewFolder\Abc.html to D:\\NewFolder\\Abc.html which then gets correctly interpreted in your Javascript.

Reading Java Properties file without escaping values

My application needs to use a .properties file for configuration.
In the properties files, users are allow to specify paths.
Problem
Properties files need values to be escaped, eg
dir = c:\\mydir
Needed
I need some way to accept a properties file where the values are not escaped, so that the users can specify:
dir = c:\mydir
Why not simply extend the properties class to incorporate stripping of double forward slashes. A good feature of this will be that through the rest of your program you can still use the original Properties class.
public class PropertiesEx extends Properties {
public void load(FileInputStream fis) throws IOException {
Scanner in = new Scanner(fis);
ByteArrayOutputStream out = new ByteArrayOutputStream();
while(in.hasNext()) {
out.write(in.nextLine().replace("\\","\\\\").getBytes());
out.write("\n".getBytes());
}
InputStream is = new ByteArrayInputStream(out.toByteArray());
super.load(is);
}
}
Using the new class is a simple as:
PropertiesEx p = new PropertiesEx();
p.load(new FileInputStream("C:\\temp\\demo.properties"));
p.list(System.out);
The stripping code could also be improved upon but the general principle is there.
Two options:
use the XML properties format instead
Writer your own parser for a modified .properties format without escapes
You can "preprocess" the file before loading the properties, for example:
public InputStream preprocessPropertiesFile(String myFile) throws IOException{
Scanner in = new Scanner(new FileReader(myFile));
ByteArrayOutputStream out = new ByteArrayOutputStream();
while(in.hasNext())
out.write(in.nextLine().replace("\\","\\\\").getBytes());
return new ByteArrayInputStream(out.toByteArray());
}
And your code could look this way
Properties properties = new Properties();
properties.load(preprocessPropertiesFile("path/myfile.properties"));
Doing this, your .properties file would look like you need, but you will have the properties values ready to use.
*I know there should be better ways to manipulate files, but I hope this helps.
The right way would be to provide your users with a property file editor (or a plugin for their favorite text editor) which allows them entering the text as pure text, and would save the file in the property file format.
If you don't want this, you are effectively defining a new format for the same (or a subset of the) content model as the property files have.
Go the whole way and actually specify your format, and then think about a way to either
transform the format to the canonical one, and then use this for loading the files, or
parse this format and populate a Properties object from it.
Both of these approaches will only work directly if you actually can control your property object's creation, otherwise you will have to store the transformed format with your application.
So, let's see how we can define this. The content model of normal property files is simple:
A map of string keys to string values, both allowing arbitrary Java strings.
The escaping which you want to avoid serves just to allow arbitrary Java strings, and not just a subset of these.
An often sufficient subset would be:
A map of string keys (not containing any whitespace, : or =) to string values (not containing any leading or trailing white space or line breaks).
In your example dir = c:\mydir, the key would be dir and the value c:\mydir.
If we want our keys and values to contain any Unicode character (other than the forbidden ones mentioned), we should use UTF-8 (or UTF-16) as the storage encoding - since we have no way to escape characters outside of the storage encoding. Otherwise, US-ASCII or ISO-8859-1 (as normal property files) or any other encoding supported by Java would be enough, but make sure to include this in your specification of the content model (and make sure to read it this way).
Since we restricted our content model so that all "dangerous" characters are out of the way, we can now define the file format simply as this:
<simplepropertyfile> ::= (<line> <line break> )*
<line> ::= <comment> | <empty> | <key-value>
<comment> ::= <space>* "#" < any text excluding line breaks >
<key-value> ::= <space>* <key> <space>* "=" <space>* <value> <space>*
<empty> ::= <space>*
<key> ::= < any text excluding ':', '=' and whitespace >
<value> ::= < any text starting and ending not with whitespace,
not including line breaks >
<space> ::= < any whitespace, but not a line break >
<line break> ::= < one of "\n", "\r", and "\r\n" >
Every \ occurring in either key or value now is a real backslash, not anything which escapes something else.
Thus, for transforming it into the original format, we simply need to double it, like Grekz proposed, for example in a filtering reader:
public DoubleBackslashFilter extends FilterReader {
private boolean bufferedBackslash = false;
public DoubleBackslashFilter(Reader org) {
super(org);
}
public int read() {
if(bufferedBackslash) {
bufferedBackslash = false;
return '\\';
}
int c = super.read();
if(c == '\\')
bufferedBackslash = true;
return c;
}
public int read(char[] buf, int off, int len) {
int read = 0;
if(bufferedBackslash) {
buf[off] = '\\';
read++;
off++;
len --;
bufferedBackslash = false;
}
if(len > 1) {
int step = super.read(buf, off, len/2);
for(int i = 0; i < step; i++) {
if(buf[off+i] == '\\') {
// shift everything from here one one char to the right.
System.arraycopy(buf, i, buf, i+1, step - i);
// adjust parameters
step++; i++;
}
}
read += step;
}
return read;
}
}
Then we would pass this Reader to our Properties object (or save the contents to a new file).
Instead, we could simply parse this format ourselves.
public Properties parse(Reader in) {
BufferedReader r = new BufferedReader(in);
Properties prop = new Properties();
Pattern keyValPattern = Pattern.compile("\s*=\s*");
String line;
while((line = r.readLine()) != null) {
line = line.trim(); // remove leading and trailing space
if(line.equals("") || line.startsWith("#")) {
continue; // ignore empty and comment lines
}
String[] kv = line.split(keyValPattern, 2);
// the pattern also grabs space around the separator.
if(kv.length < 2) {
// no key-value separator. TODO: Throw exception or simply ignore this line?
continue;
}
prop.setProperty(kv[0], kv[1]);
}
r.close();
return prop;
}
Again, using Properties.store() after this, we can export it in the original format.
Based on #Ian Harrigan, here is a complete solution to get Netbeans properties file (and other escaping properties file) right from and to ascii text-files :
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
/**
* This class allows to handle Netbeans properties file.
* It is based on the work of : http://stackoverflow.com/questions/6233532/reading-java-properties-file-without-escaping-values.
* It overrides both load methods in order to load a netbeans property file, taking into account the \ that
* were escaped by java properties original load methods.
* #author stephane
*/
public class NetbeansProperties extends Properties {
#Override
public synchronized void load(Reader reader) throws IOException {
BufferedReader bfr = new BufferedReader( reader );
ByteArrayOutputStream out = new ByteArrayOutputStream();
String readLine = null;
while( (readLine = bfr.readLine()) != null ) {
out.write(readLine.replace("\\","\\\\").getBytes());
out.write("\n".getBytes());
}//while
InputStream is = new ByteArrayInputStream(out.toByteArray());
super.load(is);
}//met
#Override
public void load(InputStream is) throws IOException {
load( new InputStreamReader( is ) );
}//met
#Override
public void store(Writer writer, String comments) throws IOException {
PrintWriter out = new PrintWriter( writer );
if( comments != null ) {
out.print( '#' );
out.println( comments );
}//if
List<String> listOrderedKey = new ArrayList<String>();
listOrderedKey.addAll( this.stringPropertyNames() );
Collections.sort(listOrderedKey );
for( String key : listOrderedKey ) {
String newValue = this.getProperty(key);
out.println( key+"="+newValue );
}//for
}//met
#Override
public void store(OutputStream out, String comments) throws IOException {
store( new OutputStreamWriter(out), comments );
}//met
}//class
You could try using guava's Splitter: split on '=' and build a map from resulting Iterable.
The disadvantage of this solution is that it does not support comments.
#pdeva: one more solution
//Reads entire file in a String
//available in java1.5
Scanner scan = new Scanner(new File("C:/workspace/Test/src/myfile.properties"));
scan.useDelimiter("\\Z");
String content = scan.next();
//Use apache StringEscapeUtils.escapeJava() method to escape java characters
ByteArrayInputStream bi=new ByteArrayInputStream(StringEscapeUtils.escapeJava(content).getBytes());
//load properties file
Properties properties = new Properties();
properties.load(bi);
It's not an exact answer to your question, but a different solution that may be appropriate to your needs. In Java, you can use / as a path separator and it'll work on both Windows, Linux, and OSX. This is specially useful for relative paths.
In your example, you could use:
dir = c:/mydir

Generating a canonical path

Does any one know of any Java libraries I could use to generate canonical paths (basically remove back-references).
I need something that will do the following:
Raw Path -> Canonical Path
/../foo/ -> /foo
/foo/ -> /foo
/../../../ -> /
/./foo/./ -> /foo
//foo//bar -> /foo/bar
//foo/../bar -> /bar
etc...
At the moment I lazily rely on using:
new File("/", path).getCanonicalPath();
But this resolves the path against the actual file system, and is synchronised.
java.lang.Thread.State: BLOCKED (on object monitor)
at java.io.ExpiringCache.get(ExpiringCache.java:55)
- waiting to lock <0x93a0d180> (a java.io.ExpiringCache)
at java.io.UnixFileSystem.canonicalize(UnixFileSystem.java:137)
at java.io.File.getCanonicalPath(File.java:559)
The paths that I am canonicalising do not exist on my file system, so just the logic of the method will do me fine, thus not requiring any synchronisation. I'm hoping for a well tested library rather than having to write my own.
I think you can use the URI class to do this; e.g. if the path contains no characters that need escaping in a URI path component, you can do this.
String normalized = new URI(path).normalize().getPath();
If the path contains (or might contain) characters that need escaping, the multi-argument constructors will escape the path argument, and you can provide null for the other arguments.
Notes:
The above normalizes a file path by treating it as a relative URI. If you want to normalize an entire URI ... including the (optional) scheme, authority, and other components, don't call getPath()!
URI normalization does not involve looking at the file system as File canonicalization does. But the flip side is that normalization behaves differently to canonicalization when there are symbolic links in the path.
Using Apache Commons IO (a well-known and well-tested library)
public static String normalize(String filename)
will do exactly what you're looking for.
Example:
String result = FilenameUtils.normalize(myFile.getAbsolutePath());
If you don't need path canonization but only normalization, in Java 7 you can use java.nio.file.Path.normalize method.
According to http://docs.oracle.com/javase/7/docs/api/java/nio/file/Path.html:
This method does not access the file system; the path may not locate a file that exists.
If you work with File object you can use something like this:
file.toPath().normalize().toFile()
You could try an algorithm like this:
String collapsePath(String path) {
/* Split into directory parts */
String[] directories = path.split("/");
String[] newDirectories = new String[directories.length];
int i, j = 0;
for (i=0; i<directories.length; i++) {
/* Ignore the previous directory if it is a double dot */
if (directories[i].equals("..") && j > 0)
newDirectories[j--] = "";
/* Completely ignore single dots */
else if (! directories[i].equals("."))
newDirectories[j++] = directories[i];
}
/* Ah, what I would give for String.join() */
String newPath = new String();
for (i=0; i < j; i++)
newPath = newPath + "/" + newDirectories[i];
return newPath;
}
It isn't perfect; it's linear over the number of directories but does make a copy in memory.
Which kind of path is qualified as a Canonical Path is OS dependent.
That's why Java need to check it on the filesystem.
So there's no simple logic to test the path without knowing the OS.
So, while normalizing can do the trick, here is a procedure that exposes a little more of the Java API than would simply calling Paths.normalize()
Say I want to find a file that is not in my current directory on the file system.
My working code file is
myproject/src/JavaCode.java
Located in myproject/src/. My file is in
../../data/myfile.txt
I'm testing my program running my code from JavaCode.java
public static void main(String[] args) {
findFile("../../data","myfile.txt");
System.out.println("Found it.");
}
public static File findFile(String inputPath, String inputFile) {
File dataDir = new File("").getAbsoluteFile(); // points dataDir to working directory
String delimiters = "" + '\\' + '/'; // dealing with different system separators
StringTokenizer st = new StringTokenizer(inputPath, delimiters);
while(st.hasMoreTokens()) {
String s = st.nextToken();
if(s.trim().isEmpty() || s.equals("."))
continue;
else if(s.equals(".."))
dataDir = dataDir.getParentFile();
else {
dataDir = new File(dataDir, s);
if(!dataDir.exists())
throw new RuntimeException("Data folder does not exist.");
}
}
return new File(dataDir, inputFile);
}
Having placed a file at the specified location, this should print "Found it."
I'm assuming you have strings and you want strings, and you have Java 7 available now, and your default file system uses '/' as a path separator, so try:
String output = FileSystems.getDefault().getPath(input).normalize().toString();
You can try this out with:
/**
* Input Output
* /../foo/ -> /foo
* /foo/ -> /foo
* /../../../ -> /
* /./foo/./ -> /foo
* //foo//bar -> /foo/bar
* //foo/../bar -> /bar
*/
#Test
public void testNormalizedPath() throws URISyntaxException, IOException {
String[] in = new String[]{"/../foo/", "/foo/", "/../../../", "/./foo/./",
"//foo/bar", "//foo/../bar", "/", "/foo"};
String[] ex = new String[]{"/foo", "/foo", "/", "/foo", "/foo/bar", "/bar", "/", "/foo"};
FileSystem fs = FileSystems.getDefault();
for (int i = 0; i < in.length; i++) {
assertEquals(ex[i], fs.getPath(in[i]).normalize().toString());
}
}

Categories