mnaufaldillah

StringFromFile Jmeter

Oct 23rd, 2021
612
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to you under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  * http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17.  
  18. package org.apache.jmeter.functions;
  19.  
  20. import java.io.BufferedReader;
  21. import java.io.IOException;
  22. import java.nio.file.Files;
  23. import java.nio.file.Paths;
  24. import java.text.DecimalFormat;
  25. import java.util.ArrayList;
  26. import java.util.Collection;
  27. import java.util.List;
  28.  
  29. import org.apache.commons.io.IOUtils;
  30. import org.apache.jmeter.engine.util.CompoundVariable;
  31. import org.apache.jmeter.samplers.SampleResult;
  32. import org.apache.jmeter.samplers.Sampler;
  33. import org.apache.jmeter.testelement.TestStateListener;
  34. import org.apache.jmeter.threads.JMeterVariables;
  35. import org.apache.jmeter.util.JMeterUtils;
  36. import org.apache.jorphan.util.JMeterStopThreadException;
  37. import org.slf4j.Logger;
  38. import org.slf4j.LoggerFactory;
  39.  
  40. /**
  41.  * <p>StringFromFile Function to read a String from a text file.</p>
  42.  *
  43.  * Parameters:
  44.  * <ul>
  45.  *   <li>file name</li>
  46.  *   <li>variable name (optional - defaults to {@code StringFromFile_})</li>
  47.  *   <li>sequence start</li>
  48.  *   <li>sequence end</li>
  49.  * </ul>
  50.  *
  51.  * Returns:
  52.  * <ul>
  53.  *   <li>the next line from the file</li>
  54.  *   <li>or {@code **ERR**} if an error occurs</li>
  55.  *   <li>value is also saved in the variable for later re-use.</li>
  56.  * </ul>
  57.  *
  58.  * <p>Ensure that different variable names are used for each call to the function</p>
  59.  *
  60.  *
  61.  * Notes:
  62.  * <ul>
  63.  * <li>JMeter instantiates a single copy of each function for every reference in the test plan</li>
  64.  * <li>Function instances are shared between threads.</li>
  65.  * <li>Each StringFromFile instance reads the file independently. The output variable can be used to save the
  66.  * value for later use in the same thread.</li>
  67.  * <li>The file name is resolved at file (re-)open time; the file is initially opened on first execution (which could be any thread)</li>
  68.  * <li>the output variable name is resolved every time the function is invoked</li>
  69.  * </ul>
  70.  * Because function instances are shared, it does not make sense to use the thread number as part of the file name.
  71.  * @since 1.9
  72.  */
  73. public class StringFromFile extends AbstractFunction implements TestStateListener {
  74.     private static final Logger log = LoggerFactory.getLogger(StringFromFile.class);
  75.  
  76.     // Only modified by static block so no need to synchronize subsequent read-only access
  77.     private static final List<String> desc = new ArrayList<>();
  78.  
  79.     private static final String KEY = "__StringFromFile";//$NON-NLS-1$
  80.  
  81.     static final String ERR_IND = "**ERR**";//$NON-NLS-1$
  82.  
  83.     static {
  84.         desc.add(JMeterUtils.getResString("string_from_file_file_name"));//$NON-NLS-1$
  85.         desc.add(JMeterUtils.getResString("function_name_paropt"));//$NON-NLS-1$
  86.         desc.add(JMeterUtils.getResString("string_from_file_seq_start"));//$NON-NLS-1$
  87.         desc.add(JMeterUtils.getResString("string_from_file_seq_final"));//$NON-NLS-1$
  88.     }
  89.  
  90.     private static final int MIN_PARAM_COUNT = 1;
  91.  
  92.     private static final int PARAM_NAME = 2;
  93.  
  94.     private static final int PARAM_START = 3;
  95.  
  96.     private static final int PARAM_END = 4;
  97.  
  98.     private static final int MAX_PARAM_COUNT = 4;
  99.  
  100.     private static final int COUNT_UNUSED = -2;
  101.  
  102.     // @GuardedBy("this")
  103.     private Object[] values;
  104.  
  105.     // @GuardedBy("this")
  106.     private BufferedReader myBread = null; // Buffered reader
  107.  
  108.     // @GuardedBy("this")
  109.     private boolean firstTime = false; // should we try to open the file?
  110.  
  111.     // @GuardedBy("this")
  112.     private String fileName; // needed for error messages
  113.  
  114.     // @GuardedBy("this")
  115.     private int myStart = COUNT_UNUSED;
  116.  
  117.     // @GuardedBy("this")
  118.     private int myCurrent = COUNT_UNUSED;
  119.  
  120.     // @GuardedBy("this")
  121.     private int myEnd = COUNT_UNUSED;
  122.  
  123.     public StringFromFile() {
  124.         if (log.isDebugEnabled()) {
  125.             log.debug("++++++++ Construct {}" + this);
  126.         }
  127.     }
  128.  
  129.     /**
  130.      * Close file and log
  131.      */
  132.     private synchronized void closeFile() {
  133.         if (myBread == null) {
  134.             return;
  135.         }
  136.         if (log.isInfoEnabled()) {
  137.             log.info("{} closing file {}", Thread.currentThread().getName(), fileName);//$NON-NLS-1$
  138.         }
  139.         try {
  140.             myBread.close();
  141.         } catch (IOException e) {
  142.             log.error("closeFile() error: {}", e.toString(), e);//$NON-NLS-1$
  143.         }
  144.     }
  145.  
  146.     private synchronized void openFile() {
  147.         String tn = Thread.currentThread().getName();
  148.         fileName = ((CompoundVariable) values[0]).execute();
  149.  
  150.         String start = "";
  151.         if (values.length >= PARAM_START) {
  152.             start = ((CompoundVariable) values[PARAM_START - 1]).execute();
  153.             try {
  154.                 // Low chances to be non numeric, we parse
  155.                 myStart = Integer.parseInt(start);
  156.             } catch(NumberFormatException e) {
  157.                 myStart = COUNT_UNUSED;// Don't process invalid numbers
  158.                 log.warn("Exception parsing {} as int, value will not be considered as Start Number sequence", start);
  159.             }
  160.         }
  161.         // Have we used myCurrent yet?
  162.         // Set to 1 if start number is missing (to allow for end without start)
  163.         if (myCurrent == COUNT_UNUSED) {
  164.             myCurrent = myStart == COUNT_UNUSED ? 1 : myStart;
  165.         }
  166.  
  167.         if (values.length >= PARAM_END) {
  168.             String tmp = ((CompoundVariable) values[PARAM_END - 1]).execute();
  169.             try {
  170.                 // Low chances to be non numeric, we parse
  171.                 myEnd = Integer.parseInt(tmp);
  172.             } catch(NumberFormatException e) {
  173.                 myEnd = COUNT_UNUSED;// Don't process invalid numbers (including "")
  174.                 log.warn("Exception parsing {} as int, value will not be considered as End Number sequence", tmp);
  175.             }
  176.         }
  177.  
  178.         if (values.length >= PARAM_START) {
  179.             if (log.isInfoEnabled()) {
  180.                 log.info("{} Start = {} Current = {} End = {}", tn, myStart, myCurrent, myEnd);//$NON-NLS-1$
  181.             }
  182.             if (myEnd != COUNT_UNUSED) {
  183.                 if (myCurrent > myEnd) {
  184.                     if (log.isInfoEnabled()) {
  185.                         log.info("{} No more files to process, {} > {}", tn, myCurrent, myEnd);//$NON-NLS-1$
  186.                     }
  187.                     myBread = null;
  188.                     return;
  189.                 }
  190.             }
  191.             /*
  192.              * DecimalFormat adds the number to the end of the format if there
  193.              * are no formatting characters, so we need a way to prevent this
  194.              * from messing up the file name.
  195.              *
  196.              */
  197.             if (myStart != COUNT_UNUSED) // Only try to format if there is a
  198.                                             // number
  199.             {
  200.                 log.info("{} using format {}", tn, fileName);
  201.                 try {
  202.                     DecimalFormat myFormatter = new DecimalFormat(fileName);
  203.                     fileName = myFormatter.format(myCurrent);
  204.                 } catch (NumberFormatException e) {
  205.                     log.warn("Bad file name format ", e);
  206.                 }
  207.             }
  208.             myCurrent++;// for next time
  209.         }
  210.  
  211.         log.info("{} opening file {}", tn, fileName);//$NON-NLS-1$
  212.         try {
  213.             myBread = Files.newBufferedReader(Paths.get(fileName));
  214.         } catch (Exception e) {
  215.             log.error("openFile() error: {}", e.toString());//$NON-NLS-1$
  216.             IOUtils.closeQuietly(myBread, null);
  217.             myBread = null;
  218.         }
  219.     }
  220.  
  221.     /** {@inheritDoc} */
  222.     @Override
  223.     public synchronized String execute(SampleResult previousResult, Sampler currentSampler)
  224.             throws InvalidVariableException {
  225.         String myValue = ERR_IND;
  226.         String myName = "StringFromFile_";//$NON-NLS-1$
  227.         if (values.length >= PARAM_NAME) {
  228.             myName = ((CompoundVariable) values[PARAM_NAME - 1]).execute().trim();
  229.         }
  230.  
  231.         /*
  232.          * To avoid re-opening the file repeatedly after an error, only try to
  233.          * open it in the first execute() call (It may be re=opened at EOF, but
  234.          * that will cause at most one failure.)
  235.          */
  236.         if (firstTime) {
  237.             openFile();
  238.             firstTime = false;
  239.         }
  240.  
  241.         if (null != myBread) { // Did we open the file?
  242.             try {
  243.                 String line = myBread.readLine();
  244.                 if (line == null) { // EOF, re-open file
  245.                     String tn = Thread.currentThread().getName();
  246.                     log.info("{} EOF on  file {}", tn, fileName);//$NON-NLS-1$
  247.                     closeFile();
  248.                     openFile();
  249.                     if (myBread != null) {
  250.                         line = myBread.readLine();
  251.                     } else {
  252.                         line = ERR_IND;
  253.                         if (myEnd != COUNT_UNUSED) {// Are we processing a file
  254.                                                     // sequence?
  255.                             log.info("{} Detected end of sequence.", tn);
  256.                             throw new JMeterStopThreadException("End of sequence");
  257.                         }
  258.                     }
  259.                 }
  260.                 myValue = line;
  261.             } catch (IOException e) {
  262.                 String tn = Thread.currentThread().getName();
  263.                 log.error("{} error reading file {}", tn, e.toString());//$NON-NLS-1$
  264.             }
  265.         } else { // File was not opened successfully
  266.             if (myEnd != COUNT_UNUSED) {// Are we processing a file sequence?
  267.                 if (log.isInfoEnabled()) {
  268.                     log.info("{} Detected end of sequence.", Thread.currentThread().getName());
  269.                 }
  270.                 throw new JMeterStopThreadException("End of sequence");
  271.             }
  272.         }
  273.  
  274.         if (myName.length() > 0) {
  275.             JMeterVariables vars = getVariables();
  276.             if (vars != null) {// Can be null if called from Config item testEnded() method
  277.                 vars.put(myName, myValue);
  278.             }
  279.         }
  280.  
  281.         if (log.isDebugEnabled()) {
  282.             log.debug("{} name:{} value:{}", Thread.currentThread().getName(), myName, myValue); //$NON-NLS-1$
  283.         }
  284.  
  285.         return myValue;
  286.  
  287.     }
  288.  
  289.     /** {@inheritDoc} */
  290.     @Override
  291.     public synchronized void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException {
  292.  
  293.         log.debug("{}::StringFromFile.setParameters()", this);//$NON-NLS-1$
  294.         checkParameterCount(parameters, MIN_PARAM_COUNT, MAX_PARAM_COUNT);
  295.         values = parameters.toArray();
  296.  
  297.         StringBuilder sb = new StringBuilder(40);
  298.         sb.append("setParameters(");//$NON-NLS-1$
  299.         for (int i = 0; i < values.length; i++) {
  300.             if (i > 0) {
  301.                 sb.append(',');
  302.             }
  303.             sb.append(((CompoundVariable) values[i]).getRawParameters());
  304.         }
  305.         sb.append(')');//$NON-NLS-1$
  306.         log.info("{}", sb);
  307.  
  308.         // N.B. setParameters is called before the test proper is started,
  309.         // and thus variables are not interpreted at this point
  310.         // So defer the file open until later to allow variable file names to be
  311.         // used.
  312.         firstTime = true;
  313.     }
  314.  
  315.     /** {@inheritDoc} */
  316.     @Override
  317.     public String getReferenceKey() {
  318.         return KEY;
  319.     }
  320.  
  321.     /** {@inheritDoc} */
  322.     @Override
  323.     public List<String> getArgumentDesc() {
  324.         return desc;
  325.     }
  326.  
  327.     /** {@inheritDoc} */
  328.     @Override
  329.     public void testStarted() {
  330.         //
  331.     }
  332.  
  333.     /** {@inheritDoc} */
  334.     @Override
  335.     public void testStarted(String host) {
  336.         //
  337.     }
  338.  
  339.     /** {@inheritDoc} */
  340.     @Override
  341.     public void testEnded() {
  342.         this.testEnded(""); //$NON-NLS-1$
  343.     }
  344.  
  345.     /** {@inheritDoc} */
  346.     @Override
  347.     public void testEnded(String host) {
  348.         closeFile();
  349.     }
  350.  
  351. }
RAW Paste Data