Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package org.apache.jmeter.config; import java.text.DecimalFormat; import java.util.Random; import org.apache.commons.lang3.math.NumberUtils; import org.apache.jmeter.engine.event.LoopIterationEvent; import org.apache.jmeter.engine.event.LoopIterationListener; import org.apache.jmeter.engine.util.NoConfigMerge; import org.apache.jmeter.engine.util.NoThreadClone; import org.apache.jmeter.testbeans.TestBean; import org.apache.jmeter.threads.JMeterContextService; import org.apache.jmeter.threads.JMeterVariables; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; public class RandomVariableConfig extends ConfigTestElement implements TestBean, LoopIterationListener, NoThreadClone, NoConfigMerge { private static final Logger log = LoggingManager.getLoggerForClass(); private static final long serialVersionUID = 233L; /* * N.B. this class is shared between threads (NoThreadClone) so all access to variables * needs to be protected by a lock (either sync. or volatile) to ensure safe publication. */ private String minimumValue; private String maximumValue; private String variableName; private String outputFormat; private String randomSeed; private boolean perThread; // This class is not cloned per thread, so this is shared private Random globalRandom = null; // Used for per-thread/user numbers // Cannot be static, as random numbers are not to be shared between instances private transient ThreadLocal<Random> perThreadRandom = initThreadLocal(); private ThreadLocal<Random> initThreadLocal() { return new ThreadLocal<Random>() { @Override protected Random initialValue() { init(); return new Random(getRandomSeedAsLong()); } }; } private int n; private long minimum; private Object readResolve() { perThreadRandom = initThreadLocal(); return this; } /* * nextInt(n) returns values in the range [0,n), * so n must be set to max-min+1 */ private void init() { final String minAsString = getMinimumValue(); minimum = NumberUtils.toLong(minAsString); final String maxAsString = getMaximumValue(); long maximum = NumberUtils.toLong(maxAsString); long rangeL = maximum - minimum + 1; // This can overflow if (minimum >= maximum) { log.error("maximum(" + maxAsString + ") must be > minimum" + minAsString + ")"); n = 0;// This is used as an error indicator return; } if (rangeL > Integer.MAX_VALUE || rangeL <= 0) {// check for overflow too log.warn("maximum(" + maxAsString + ") - minimum" + minAsString + ") must be <=" + Integer.MAX_VALUE); rangeL = Integer.MAX_VALUE; } n = (int) rangeL; } /** {@inheritDoc} */ @Override public void iterationStart(LoopIterationEvent iterEvent) { Random randGen = null; if (getPerThread()) { randGen = perThreadRandom.get(); } else { synchronized (this) { if (globalRandom == null) { init(); globalRandom = new Random(getRandomSeedAsLong()); } randGen = globalRandom; } } if (n <= 0) { return; } long nextRand = minimum + randGen.nextInt(n); // Cannot use getThreadContext() as we are not cloned per thread JMeterVariables variables = JMeterContextService.getContext().getVariables(); variables.put(getVariableName(), formatNumber(nextRand)); } // Use format to create number; if it fails, use the default private String formatNumber(long value) { String format = getOutputFormat(); if (format != null && format.length() > 0) { try { DecimalFormat myFormatter = new DecimalFormat(format); return myFormatter.format(value); } catch (IllegalArgumentException ignored) { log.warn("Exception formatting value:" + value + " at format:" + format + ", using default"); } } return Long.toString(value); } /** * @return the minValue */ public synchronized String getMinimumValue() { return minimumValue; } /** * @param minValue the minValue to set */ public synchronized void setMinimumValue(String minValue) { this.minimumValue = minValue; } /** * @return the maxvalue */ public synchronized String getMaximumValue() { return maximumValue; } /** * @param maxvalue the maxvalue to set */ public synchronized void setMaximumValue(String maxvalue) { this.maximumValue = maxvalue; } /** * @return the variableName */ public synchronized String getVariableName() { return variableName; } /** * @param variableName the variableName to set */ public synchronized void setVariableName(String variableName) { this.variableName = variableName; } /** * @return the randomSeed */ public synchronized String getRandomSeed() { return randomSeed; } /** * @return the randomSeed as a long */ private synchronized long getRandomSeedAsLong() { long seed = 0; if (randomSeed.length() == 0) { seed = System.currentTimeMillis(); } else { try { seed = Long.parseLong(randomSeed); } catch (NumberFormatException e) { seed = System.currentTimeMillis(); log.warn("Cannot parse seed " + e.getLocalizedMessage()); } } return seed; } /** * @param randomSeed the randomSeed to set */ public synchronized void setRandomSeed(String randomSeed) { this.randomSeed = randomSeed; } /** * @return the perThread */ public synchronized boolean getPerThread() { return perThread; } /** * @param perThread the perThread to set */ public synchronized void setPerThread(boolean perThread) { this.perThread = perThread; } /** * @return the outputFormat */ public synchronized String getOutputFormat() { return outputFormat; } /** * @param outputFormat the outputFormat to set */ public synchronized void setOutputFormat(String outputFormat) { this.outputFormat = outputFormat; } }