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, WITHOUT 13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 * License for the specific language governing permissions and limitations 15 * under the License. 16 * 17 */ 18 19 package org.apache.jmeter.config; 20 21 import java.text.DecimalFormat; 22 import java.util.Random; 23 24 import org.apache.commons.lang.math.NumberUtils; 25 import org.apache.jmeter.engine.event.LoopIterationEvent; 26 import org.apache.jmeter.engine.event.LoopIterationListener; 27 import org.apache.jmeter.engine.util.NoThreadClone; 28 import org.apache.jmeter.testbeans.TestBean; 29 import org.apache.jmeter.threads.JMeterContextService; 30 import org.apache.jmeter.threads.JMeterVariables; 31 import org.apache.jorphan.logging.LoggingManager; 32 import org.apache.log.Logger; 33 34 public class RandomVariableConfig extends ConfigTestElement 35 implements TestBean, LoopIterationListener, NoThreadClone 36 { 37 private static final Logger log = LoggingManager.getLoggerForClass(); 38 39 private static final long serialVersionUID = 233L; 40 41 private String minimumValue; 42 43 private String maximumValue; 44 45 private String variableName; 46 47 private String outputFormat; 48 49 private String randomSeed; 50 51 private boolean perThread; 52 53 // This class is not cloned per thread, so this is shared 54 private Random globalRandom = null; 55 56 // Used for per-thread/user numbers 57 private transient ThreadLocal perThreadRandom = new ThreadLocal() { 58 protected Object initialValue() { 59 init(); 60 return new Random(getRandomSeedAsLong()); 61 }}; 62 63 private int n; 64 private long minimum; 65 66 /* 67 * nextInt(n) returns values in the range [0,n), 68 * so n must be set to max-min+1 69 */ 70 private void init(){ 71 final String minAsString = getMinimumValue(); 72 minimum = NumberUtils.toLong(minAsString); 73 final String maxAsString = getMaximumValue(); 74 long maximum = NumberUtils.toLong(maxAsString); 75 long rangeL=maximum-minimum+1; // This can overflow 76 if (minimum >= maximum){ 77 log.error("maximum("+maxAsString+") must be > minimum"+minAsString+")"); 78 n=0;// This is used as an error indicator 79 return; 80 } 81 if (rangeL > Integer.MAX_VALUE || rangeL <= 0){// check for overflow too 82 log.warn("maximum("+maxAsString+") - minimum"+minAsString+") must be <="+Integer.MAX_VALUE); 83 rangeL=Integer.MAX_VALUE; 84 } 85 n = (int)rangeL; 86 } 87 public void iterationStart(LoopIterationEvent iterEvent) { 88 Random randGen=null; 89 if (getPerThread()){ 90 randGen = (Random) perThreadRandom.get(); 91 } else { 92 synchronized(this){ 93 if (globalRandom == null){ 94 init(); 95 globalRandom = new Random(getRandomSeedAsLong()); 96 } 97 randGen=globalRandom; 98 } 99 } 100 if (n <=0){ 101 return; 102 } 103 long nextRand = minimum + randGen.nextInt(n); 104 // Cannot use getThreadContext() as we are not cloned per thread 105 JMeterVariables variables = JMeterContextService.getContext().getVariables(); 106 variables.put(getVariableName(), formatNumber(nextRand)); 107 } 108 109 // Use format to create number; if it fails, use the default 110 private String formatNumber(long value){ 111 String format = getOutputFormat(); 112 if (format != null && format.length() > 0) { 113 try { 114 DecimalFormat myFormatter = new DecimalFormat(format); 115 return myFormatter.format(value); 116 } catch (NumberFormatException ignored) { 117 } catch (IllegalArgumentException ignored) { 118 } 119 } 120 return Long.toString(value); 121 } 122 123 /** 124 * @return the minValue 125 */ 126 public synchronized String getMinimumValue() { 127 return minimumValue; 128 } 129 130 /** 131 * @param minValue the minValue to set 132 */ 133 public synchronized void setMinimumValue(String minValue) { 134 this.minimumValue = minValue; 135 } 136 137 /** 138 * @return the maxvalue 139 */ 140 public synchronized String getMaximumValue() { 141 return maximumValue; 142 } 143 144 /** 145 * @param maxvalue the maxvalue to set 146 */ 147 public synchronized void setMaximumValue(String maxvalue) { 148 this.maximumValue = maxvalue; 149 } 150 151 /** 152 * @return the variableName 153 */ 154 public synchronized String getVariableName() { 155 return variableName; 156 } 157 158 /** 159 * @param variableName the variableName to set 160 */ 161 public synchronized void setVariableName(String variableName) { 162 this.variableName = variableName; 163 } 164 165 /** 166 * @return the randomSeed 167 */ 168 public synchronized String getRandomSeed() { 169 return randomSeed; 170 } 171 172 /** 173 * @return the randomSeed as a long 174 */ 175 private synchronized long getRandomSeedAsLong() { 176 long seed = 0; 177 if (randomSeed.length()==0){ 178 seed = System.currentTimeMillis(); 179 } else { 180 try { 181 seed = Long.parseLong(randomSeed); 182 } catch (NumberFormatException e) { 183 seed = System.currentTimeMillis(); 184 log.warn("Cannot parse seed "+e.getLocalizedMessage()); 185 } 186 } 187 return seed; 188 } 189 190 /** 191 * @param randomSeed the randomSeed to set 192 */ 193 public synchronized void setRandomSeed(String randomSeed) { 194 this.randomSeed = randomSeed; 195 } 196 197 /** 198 * @return the perThread 199 */ 200 public synchronized boolean getPerThread() { 201 return perThread; 202 } 203 204 /** 205 * @param perThread the perThread to set 206 */ 207 public synchronized void setPerThread(boolean perThread) { 208 this.perThread = perThread; 209 } 210 /** 211 * @return the outputFormat 212 */ 213 public synchronized String getOutputFormat() { 214 return outputFormat; 215 } 216 /** 217 * @param outputFormat the outputFormat to set 218 */ 219 public synchronized void setOutputFormat(String outputFormat) { 220 this.outputFormat = outputFormat; 221 } 222 223 }