/*******************************************************************************
* Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package de.gebit.integrity.runner.operations;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import com.google.inject.Inject;
import de.gebit.integrity.dsl.CustomOperation;
import de.gebit.integrity.operations.custom.Operation;
/**
* A random number generator. This one is special in several respects: it is automatically seeded on execution start, it
* caches randoms determined for every custom operation instance, and its seed is forwarded to forks to ensure their
* randomness is in sync with that of the master. All of this is required to safely work with random elements in
* Integrity tests.
*
* This operation doesn't have any input parameters, it just returns a random decimal number between 0 and 1.
*
* @author Rene Schneider - initial API and implementation
*
*/
public class RandomNumberOperation implements Operation<Object, Integer, BigDecimal> {
/**
* The seed value.
*/
private static long seed = new Random().nextLong();
/**
* The RNG.
*/
private static Random random = new Random(seed);
/**
* Calculated randoms are cached here.
*/
private static Map<Thread, Map<CustomOperation, BigDecimal>> calculatedRandoms = new HashMap<Thread, Map<CustomOperation, BigDecimal>>();
/**
* The operation which is processed will be injected here.
*/
@Inject
private CustomOperation operation;
private static Map<CustomOperation, BigDecimal> getCalculatedRandomsMap() {
Map<CustomOperation, BigDecimal> tempMap = calculatedRandoms.get(Thread.currentThread());
if (tempMap == null) {
tempMap = new HashMap<CustomOperation, BigDecimal>();
calculatedRandoms.put(Thread.currentThread(), tempMap);
}
return tempMap;
}
/**
* Sets a new seed and reseeds the RNG.
*
* @param aSeed
*/
public static void seed(Long aSeed) {
if (aSeed == null) {
seed = new Random().nextLong();
} else {
seed = aSeed;
}
random = new Random(seed);
}
public static long getSeed() {
return seed;
}
@Override
public BigDecimal execute(Object aPrefixParameter, Integer aPostfixParameter) {
BigDecimal tempResult = getCalculatedRandomsMap().get(operation);
if (tempResult == null) {
BigDecimal tempRand = new BigDecimal(random.nextDouble());
if (aPostfixParameter != null) {
tempResult = tempRand.setScale(aPostfixParameter, RoundingMode.HALF_UP);
} else {
tempResult = tempRand;
}
getCalculatedRandomsMap().put(operation, tempResult);
}
return tempResult;
}
}