/*
* JBoss, Home of Professional Open Source
* Copyright 2014, Red Hat and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
* @authors Andrew Dinn
*/
package org.jboss.byteman.contrib.bmunit;
import com.sun.tools.attach.AgentInitializationException;
import org.jboss.byteman.agent.install.Install;
import org.jboss.byteman.agent.install.VMInfo;
import org.jboss.byteman.agent.submit.Submit;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.Properties;
/**
* class used to model a specific BMUnit configuration
*/
public class BMUnitConfigState
{
private static BMUnitConfigState currentConfigState = null;
/**
* a global configuration state used to preserve the configuration
* associated with a test class should the BMUnitRunner temporarily
* reconfigure the state for a specific test method.
*/
private static BMUnitConfigState shadowConfigState = null;
/**
* a default configuration state used when a test class does not specify
* a configuration state. this state is initialised using the state
* associated with the first test class processed during the test run.
* it is also the state used to define parameters which configure autoload
* of the Byteman agent.
*/
public static BMUnitConfigState defaultConfigState = null;
private String agentHost;
private int agentPort;
private String loadDirectory;
private String resourceLoadDirectory;
private boolean allowConfigUpdate;
private boolean verbose;
private boolean debug;
private boolean bmunitVerbose;
private boolean inhibitAgentLoad;
private boolean policy;
private boolean dumpGeneratedClasses;
private String dumpGeneratedClassesDirectory;
private boolean dumpGeneratedClassesIntermediate;
private BMUnitConfigState previous;
private BMUnitConfigState(BMUnitConfig config, BMUnitConfigState previous) throws Exception
{
this.previous = previous;
boolean enforce = config.enforce();
// we don't need to use the same host and port as the default config
// although using something different is a tad weird
// if we have an empty host name and no previous setting then
// check for an env setting
agentHost = config.agentHost();
if (previous == null && (agentHost == null || agentHost.length() == 0)) {
agentHost = initHost();
}
// same story for agentPort as for agentHost
String agentPortString = config.agentPort();
if (previous == null && (agentPortString == null || agentPortString.length() == 0)) {
agentPort = initPort();
if (agentPort < 0) {
agentPort = 0;
}
} else {
try {
agentPort = Integer.valueOf(agentPortString);
} catch (NumberFormatException e) {
agentPort = 0;
}
if (agentPort < 0) {
agentPort = 0;
}
}
// the load dir and resource load dir can be reset but if they are
// empty and we have no previous setting then check for an env setting
loadDirectory = config.loadDirectory();
if (previous == null && (loadDirectory== null || loadDirectory.length() == 0)) {
loadDirectory = initDefaultLoadDirectory();
}
resourceLoadDirectory = config.resourceLoadDirectory();
if (previous == null && (resourceLoadDirectory== null || resourceLoadDirectory.length() == 0)) {
resourceLoadDirectory = initDefaultResourceLoadDirectory();
}
// whatever previous setting we have for config update cannot be changed
// so we need to check for enforcing mode
allowConfigUpdate = config.allowAgentConfigUpdate();
if (previous != null && allowConfigUpdate != previous.allowConfigUpdate) {
if (enforce) {
String message = "BMUnit configuration specifies incompatible settings for allowAgentConfigUpdate";
throw new Exception(message);
}
}
// if the verbose setting differs from the previous and we cannot update the config
// then we need to check for enforcing
// if there is no previous setting check and verbose is not set then allow for
// an environment setting
verbose = config.verbose();
if (previous != null && verbose != previous.verbose && !allowConfigUpdate) {
if (enforce) {
String message = "BMUnit configuration specifies incompatible settings for verbose";
throw new Exception(message);
}
}
// if we have no previous config allow the env setting to override
if (previous == null && !verbose) {
verbose = initVerbose();
}
// if the debug setting differs from the previous and we cannot update the config
// then we need to check for enforcing
debug = config.debug();
if (previous != null && debug != previous.debug && !allowConfigUpdate) {
if (enforce) {
String message = "BMUnit configuration specifies incompatible settings for debug";
throw new Exception(message);
}
}
// if we have no previous config allow the env setting to override
if (previous == null && !debug) {
debug = initDebug();
}
// we can always change the bmunit verbose setting
// if there is no previus setting and the config does nto set it
// then allow for an environment setting
bmunitVerbose = config.bmunitVerbose();
if (previous == null && !bmunitVerbose) {
bmunitVerbose = initBMUnitVerbose();
}
// if the initial config set inhibitAgentLoad then we stick with it
// if there is no previous setting check and inhibitAgentLoad is not set
// then allow for an environment setting
inhibitAgentLoad = config.inhibitAgentLoad();
if (previous != null && inhibitAgentLoad != previous.inhibitAgentLoad) {
if (enforce) {
String message = "BMUnit configuration specifies incompatible settings for inhibitAgentLoad";
throw new Exception(message);
}
}
if (previous == null && !inhibitAgentLoad) {
inhibitAgentLoad = System.getProperty(AGENT_INHIBIT) != null;
}
// whatever previous setting we have for policy cannot be changed
// so we need to check for enforcing mode
policy = config.policy();
if (previous != null && previous.policy != policy) {
if (enforce) {
String message = "BMUnit configuration specifies incompatible settings for inhibitAgentLoad";
throw new Exception(message);
}
}
// if the dumpGeneratedClasses setting differs from the previous and we cannot
// update the config then we need to check for enforcing
dumpGeneratedClasses = config.dumpGeneratedClasses();
if (previous != null && dumpGeneratedClasses != previous.dumpGeneratedClasses && !allowConfigUpdate) {
if (enforce) {
String message = "BMUnit configuration specifies incompatible settings for dumpGeneratedClasses";
throw new Exception(message);
}
}
// if we have no previous config allow the env setting to override
if (previous == null && !dumpGeneratedClasses) {
dumpGeneratedClasses = initDumpGeneratedClasses();
}
// the dump directory can be reset but if it is empty and we have
// no previous setting then check for an env setting
dumpGeneratedClassesDirectory = config.dumpGeneratedClassesDirectory();
if (previous == null && (dumpGeneratedClassesDirectory == null || dumpGeneratedClassesDirectory.length() > 0)) {
dumpGeneratedClassesDirectory = initDumpGeneratedClassesDirectory();
}
// if the dumpGeneratedClasses setting differs from the previous and we cannot
// update the config then we need to check for enforcing
dumpGeneratedClassesIntermediate = config.dumpGeneratedClassesIntermediate();
if (previous != null && dumpGeneratedClassesIntermediate != previous.dumpGeneratedClassesIntermediate && !allowConfigUpdate) {
if (enforce) {
String message = "BMUnit configuration specifies incompatible settings for dumpGeneratedClassesIntermediate";
throw new Exception(message);
}
}
}
private BMUnitConfigState(BMUnitConfigState previous) throws Exception
{
this.previous = previous;
if (previous != null ) {
agentHost = previous.agentHost;
agentPort = previous.agentPort;
loadDirectory = previous.loadDirectory;
resourceLoadDirectory = previous.resourceLoadDirectory;
allowConfigUpdate = previous.allowConfigUpdate;
verbose = previous.verbose;
bmunitVerbose = previous.bmunitVerbose;
inhibitAgentLoad = previous.inhibitAgentLoad;
policy = previous.policy;
dumpGeneratedClasses = previous.dumpGeneratedClasses;
dumpGeneratedClassesDirectory = previous.dumpGeneratedClassesDirectory;
dumpGeneratedClassesIntermediate = previous.dumpGeneratedClassesIntermediate;
} else {
agentHost = initHost();
agentPort = initPort();
loadDirectory = initDefaultLoadDirectory();
resourceLoadDirectory = initDefaultResourceLoadDirectory();
// default is to allow sys property updates
allowConfigUpdate = true;
verbose = initVerbose();
debug = initDebug();
bmunitVerbose = initBMUnitVerbose();
inhibitAgentLoad = System.getProperty(AGENT_INHIBIT) != null;
policy = initPolicy();
dumpGeneratedClasses = initDumpGeneratedClasses();
dumpGeneratedClassesDirectory = initDumpGeneratedClassesDirectory();
dumpGeneratedClassesIntermediate = initDumpGeneratedClassesIntermediate();
}
}
/**
* helper method to check whether we need to update agent properties
* when we switch config
* @param newConfigState
* @param oldConfigState
* @return
*/
private static boolean needPropertyReset(BMUnitConfigState newConfigState, BMUnitConfigState oldConfigState)
{
if (oldConfigState == null) {
// we are installing the default config
// we always install properties for this config
return true;
} else if (newConfigState == null) {
// we are de-installing the default config properties
// we always leave its properties in place
return false;
} else {
// we are switching from one config to another
// we need an update if the verbose, debug or
// dumpGeneratedClasses settings differ
if (newConfigState.verbose != oldConfigState.verbose) {
return true;
}
if (newConfigState.debug != oldConfigState.debug) {
return true;
}
if (newConfigState.dumpGeneratedClasses != oldConfigState.dumpGeneratedClasses) {
return true;
}
// if we get here and class dumping is enabled in either
// config (and hence both) then we need an update if the
// dump directory has changed or if we have enabled
// intermediate dumping.
if (newConfigState.dumpGeneratedClasses) {
String newDir = newConfigState.getDumpGeneratedClassesDirectory();
String oldDir = oldConfigState.getDumpGeneratedClassesDirectory();
if (!newDir.equals(oldDir)) {
return true;
}
if (newConfigState.dumpGeneratedClassesIntermediate != oldConfigState.dumpGeneratedClassesIntermediate) {
return true;
}
}
return false;
}
}
/**
* helper method to configure the properties to be reset
* when a config change occurs
* @param newConfigState
* @param oldConfigState
* @param props
* @return
*/
private static boolean configurePropertyReset(BMUnitConfigState newConfigState, BMUnitConfigState oldConfigState, Properties props)
{
boolean modified = false;
if (oldConfigState == null) {
// we are installing the default config
// we always install properties for this config
if (newConfigState.allowConfigUpdate) {
props.setProperty(BYTEMAN_ALLOW_CONFIG_UPDATE, "true");
}
if (newConfigState.verbose) {
props.setProperty(BYTEMAN_VERBOSE, "true");
}
if (newConfigState.debug) {
props.setProperty(BYTEMAN_DEBUG, "true");
}
if (newConfigState.dumpGeneratedClasses) {
props.setProperty(BYTEMAN_DUMP_GENERATED_CLASSES, "true");
if (newConfigState.dumpGeneratedClassesDirectory.length() > 0) {
props.setProperty(BYTEMAN_DUMP_GENERATED_CLASSES_DIRECTORY, newConfigState.dumpGeneratedClassesDirectory);
}
if (newConfigState.dumpGeneratedClassesIntermediate) {
props.setProperty(BYTEMAN_DUMP_GENERATED_CLASSES_INTERMEDIATE, "true");
}
}
return true;
} else if (newConfigState == null) {
// we are de-installing the default config properties
// we always leave its properties in place
return false;
} else {
// we are switching from one config to another
// we need to reset the verbose, debug or
// dumpGeneratedClasses settings if they differ
if (newConfigState.verbose != oldConfigState.verbose) {
props.setProperty(BYTEMAN_VERBOSE, (newConfigState.verbose ? "true" : ""));
modified = true;
}
if (newConfigState.debug != oldConfigState.debug) {
props.setProperty(BYTEMAN_DEBUG, (newConfigState.debug ? "true" : ""));
modified = true;
}
// if we are enabling dumping we may also need to set a dump directory
// and/or enable intermediate dumping
// if we are disabling dumping we may also need to clear the dump directory
// and/or disable intermediate dumping
// if dumping is enabled in both configs we still may need to reset
// the dump directory and/or reset intermediate dumping
boolean resetDump = false;
boolean resetDumpDir = false;
boolean resetIntermediateDump = false;
if (newConfigState.dumpGeneratedClasses) {
if (oldConfigState.dumpGeneratedClasses) {
// dump is still enabled so no need to reset dump
// but check for change to dump dir and intermediate dumps
String newDir = newConfigState.getDumpGeneratedClassesDirectory();
String oldDir = oldConfigState.getDumpGeneratedClassesDirectory();
resetDumpDir = (!newDir.equals(oldDir));
boolean isDumpIntermediate = newConfigState.isDumpGeneratedClassesIntermediate();
boolean wasDumpIntermediate = newConfigState.isDumpGeneratedClassesIntermediate();
resetIntermediateDump = isDumpIntermediate != wasDumpIntermediate;
} else {
// dump has just been enabled
resetDump = true;
// also check if we now have a dump dir or intermediate dumps
String dumpDir = newConfigState.getDumpGeneratedClassesDirectory();
resetDumpDir = dumpDir.length() > 0;
resetIntermediateDump = newConfigState.isDumpGeneratedClassesIntermediate();
}
} else {
if (oldConfigState.dumpGeneratedClasses) {
// dump is still disabled so no need to reset dump
// or the other properties
} else {
// dump has just been disabled
resetDump = true;
// check if we need to unset a dump dir or intermediate dumps
String dumpDir = oldConfigState.getDumpGeneratedClassesDirectory();
resetDumpDir = dumpDir.length() > 0;
resetIntermediateDump = oldConfigState.isDumpGeneratedClassesIntermediate();
}
}
if (resetDump) {
props.setProperty(BYTEMAN_DUMP_GENERATED_CLASSES, (newConfigState.dumpGeneratedClasses? "true" : ""));
modified = true;
}
if (resetDumpDir) {
props.setProperty(BYTEMAN_DUMP_GENERATED_CLASSES_DIRECTORY, newConfigState.getDumpGeneratedClassesDirectory());
modified = true;
}
if (resetIntermediateDump) {
props.setProperty(BYTEMAN_DUMP_GENERATED_CLASSES_INTERMEDIATE, (newConfigState.dumpGeneratedClassesIntermediate? "true" : ""));
modified = true;
}
}
return modified;
}
private static void uploadAgentProperties() throws Exception {
// if any Byteman config changes have been requested and
// are allowed upload all reconfigured system property
// settings to the agent.
BMUnitConfigState previousConfigState = currentConfigState.previous;
if (needPropertyReset(currentConfigState, previousConfigState)) {
Submit submit = new Submit(currentConfigState.getHost(), currentConfigState.getPort());
Properties properties = new Properties();
if (configurePropertyReset(currentConfigState, previousConfigState, properties)) {
submit.setSystemProperties(properties);
}
}
}
private static void resetAgentProperties() throws Exception {
// if we uploaded any reconfigured system property settings
// to the agent then revert them
BMUnitConfigState previousConfigState = currentConfigState.previous;
if (previousConfigState != null && previousConfigState.isAllowConfigUpdate()) {
if (needPropertyReset(previousConfigState, currentConfigState)) {
Submit submit = new Submit(currentConfigState.getHost(), currentConfigState.getPort());
Properties properties = new Properties();
if (configurePropertyReset(previousConfigState, currentConfigState, properties)) {
submit.setSystemProperties(properties);
}
}
}
}
/**
* load the agent into this JVM if not already loaded. unfortunately this can only be done if we have
* the pid of the current process and we cannot get that in a portable way
*/
private void loadAgent() throws Exception
{
String id = null;
// if we can get a proper pid on Linux we use it
int pid = getPid();
// uncomment to force lookup by name even on Linux
// pid = 0;
if (pid > 0) {
id = Integer.toString(pid);
} else {
/*
VMInfo[] vmInfo = Install.availableVMs();
// search for a JVM which looks like it is running a JUnit test
// and install the agent into that JVM
// it could be run from ant or maven or some other process!!
for (int i = 0; i < vmInfo.length; i++) {
String displayName = vmInfo[i].getDisplayName();
if (displayName.startsWith("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner ")) {
// a JUnit test forked by ant
id = vmInfo[i].getId();
break;
} else if (displayName.startsWith("org.apache.tools.ant.launch.Launcher ")) {
// a JUnit test run directly by ant
id = vmInfo[i].getId();
break;
} else {
// TODO -- identify a forked maven test and then a test run directly or any other mode of running
}
*/
// alternative strategy which will work everywhere
// set a unique system property and then check each available VM until we find it
String prop = "org.jboss.byteman.contrib.bmunit.agent.unique";
String unique = Long.toHexString(System.currentTimeMillis());
System.setProperty(prop, unique);
VMInfo[] vmInfo = Install.availableVMs();
for (int i = 0; i < vmInfo.length; i++) {
String nextId = vmInfo[i].getId();
String value = Install.getSystemProperty(nextId, prop);
if (unique.equals(value)) {
id = nextId;
break;
}
}
// last ditch effort to obtain pid on Windows where the availableVMs list may be empty
if (id == null) {
String processName = ManagementFactory.getRuntimeMXBean().getName();
if (processName != null && processName.contains("@")) {
id = processName.substring(0, processName.indexOf("@"));
// check we actually have an integer
try {
Integer.parseInt(id);
// well, it's a number so now check it identifies the current VM
String value = Install.getSystemProperty(id, prop);
if (!unique.equals(value)) {
// nope, not the right process
id = null;
}
} catch (NumberFormatException e) {
// nope, not a number
id = null;
}
}
}
// make sure we found a process
if (id == null) {
throw new Exception("BMUnit : Unable to identify test JVM process during agent load");
}
}
try {
if (isBMUnitVerbose()) {
System.out.println("BMUnit : loading agent id = " + id);
}
Properties properties = new Properties();
configurePropertyReset(currentConfigState, null, properties);
int size = properties.size();
String[] proparray = new String[size];
int i = 0;
for (String key : properties.stringPropertyNames()) {
proparray[i++] = key + "=" + properties.getProperty(key);
}
Install.install(id, true, isPolicy(), getHost(), getPort(), proparray);
} catch (AgentInitializationException e) {
// this probably indicates that the agent is already installed
}
}
/**
* return the integer process id of the current process. n.b. only works on Linux.
* @return
*/
private static int getPid()
{
File file = new File("/proc/self/stat");
if (!file.exists() || !file.canRead()) {
return 0;
}
FileInputStream fis = null;
int pid = 0;
try {
fis = new FileInputStream(file);
byte[] bytes = new byte[10];
StringBuilder builder = new StringBuilder();
fis.read(bytes);
for (int i = 0; i < 10; i++) {
char c = (char)bytes[i];
if (Character.isDigit(c)) {
builder.append(c);
} else {
break;
}
}
pid = Integer.valueOf(builder.toString());
} catch (Exception e) {
// ignore
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e1) {
// ignore
}
}
}
return pid;
}
/**
* System property which identifies the directory from which to
* start searching for rule script. If unset the current working
* directory of the test is used.
*/
public final static String LOAD_DIRECTORY = "org.jboss.byteman.contrib.bmunit.load.directory";
/**
* System property which identifies the resource load directory
* from which to start searching for rule script.
*/
public final static String RESOURCE_LOAD_DIRECTORY = "org.jboss.byteman.contrib.bmunit.resource.load.directory";
/**
* System property specifying the port to be used when starting the agent and when submitting
* rules to it. You can normally just use the default port.
*/
public final static String AGENT_PORT = "org.jboss.byteman.contrib.bmunit.agent.port";
/**
* System property specifying the host to be used when starting the agent and when submitting
* rules to it. You can normally just use the default host.
*/
public final static String AGENT_HOST = "org.jboss.byteman.contrib.bmunit.agent.host";
/**
* System property specifying whether to set a security policy when loading the agent.
*/
public final static String AGENT_POLICY = "org.jboss.byteman.contrib.bmunit.agent.policy";
/**
* System property which inhibits automatic loading of the agent. If you set this then you have to load
* the agent yourself using the Install API or ensure JUnit loads by forking a JVM and passing
* the necessary -javaagent options on the command line. You may also want to set this if you you have
* loaded the agent into a remote service in another JVM driven by your unit test.
*/
public final static String AGENT_INHIBIT = "org.jboss.byteman.contrib.bmunit.agent.inhibit";
/**
* System property which enables tracing of Byteman activity
*/
public final static String BYTEMAN_ALLOW_CONFIG_UPDATE = "org.jboss.byteman.allow.config.update";
/**
* System property which enables tracing of Byteman activity
*/
public final static String BYTEMAN_VERBOSE = "org.jboss.byteman.verbose";
/**
* System property which enables printing of Byteman rule debug statements
*/
public final static String BYTEMAN_DEBUG = "org.jboss.byteman.debug";
/**
* System property which enables tracing of bmunit activity
*/
public final static String BMUNIT_VERBOSE = "org.jboss.byteman.contrib.bmunit.verbose";
/**
* System property which enables dumping of generated classes
*/
public final static String BYTEMAN_DUMP_GENERATED_CLASSES = "org.jboss.byteman.dump.generated.classes";
/**
* System property which configures directory path for files used for dumping of generated classes
*/
public final static String BYTEMAN_DUMP_GENERATED_CLASSES_DIRECTORY = "org.jboss.byteman.dump.generated.classes.directory";
/**
* System property which configures dumping of intermediate versions of generated classes
*/
public final static String BYTEMAN_DUMP_GENERATED_CLASSES_INTERMEDIATE = "org.jboss.byteman.dump.generated.classes.intermediate";
/**
* this is only provided for backward compatibility in case some app was
* using this constant string to configure the required property.
*/
public final static String VERBOSE = BMUNIT_VERBOSE;
/**
* install the configuration for a specific test class, possibly
* also adopting it as the default configuration
*
* this method is not thread-safe. BMUnit assumes that only one
* JUnit/TestNG test is run at a time.
* @param config the config to install or null if no config is available
* @param testClass the test class which may or may not have an
* associated config
* @throws Exception if the config cannot be installed or an agent
* load error occurs
*/
public static void pushConfigurationState(BMUnitConfig config, Class<?> testClass) throws Exception
{
// current config should be null when this is called
if (currentConfigState != null) {
throw new Exception("BMUnit test class configuration pushed without prior pop!");
}
if (config != null) {
currentConfigState = new BMUnitConfigState(config, defaultConfigState);
} else {
currentConfigState = new BMUnitConfigState(defaultConfigState);
}
// if this is the first config we have seen then use it as the default
// and auto load the agent if needed
if (defaultConfigState == null) {
defaultConfigState = currentConfigState;
if (!defaultConfigState.inhibitAgentLoad) {
defaultConfigState.loadAgent();
}
} else {
// upload any properties changed by this config
uploadAgentProperties();
}
}
public static void popConfigurationState(Class<?> testClass) throws Exception
{
// current config should be non-null and shadow config null when this is called
if (currentConfigState == null) {
// should not happen
throw new Exception("BMUnit test class configuration popped without prior push!");
}
if (shadowConfigState != null) {
// should not happen
throw new Exception("BMUnit test class configuration popped without popping method configuration!");
}
// reset any properties set by this config
resetAgentProperties();
currentConfigState = null;
}
public static void pushConfigurationState(BMUnitConfig config, Method method) throws Exception
{
// current config should be non-null and shadow config null when this is called
if (currentConfigState == null) {
// should not happen
throw new Exception("BMUnit method configuration pushed without prior test configuration push!");
}
if (shadowConfigState != null) {
// should not happen
throw new Exception("BMUnit method configuration pushed without prior method configuration pop!");
}
shadowConfigState = currentConfigState;
if (config != null) {
currentConfigState = new BMUnitConfigState(config, currentConfigState);
// agent properties may need updating
uploadAgentProperties();
} else {
// agent properties will not have changed
currentConfigState = new BMUnitConfigState(currentConfigState);
}
}
public static void popConfigurationState(Method method) throws Exception
{
// shadow config should be non-null when this is called
if (shadowConfigState == null) {
// should not happen
throw new Exception("BMUnit method configuration pushed without prior method configuration pop!");
}
// agent properties may need resetting
resetAgentProperties();
currentConfigState = shadowConfigState;
shadowConfigState = null;
}
public static void resetConfigurationState(Method method) throws Exception
{
if (shadowConfigState != null) {
popConfigurationState(method);
}
}
public static void resetConfigurationState(Class<?> testClass) throws Exception
{
if (currentConfigState != null) {
popConfigurationState(testClass);
}
}
/**
* the global configuration state which configures the operation
* of BMUnit when running tests in a specific class or a method
* in that class. this value is set by BMUnitRunner before it
* starts running the tests in a test class. it may be reset
* temporarily by BMUnitRunner before executing a specific test
* method but should then be set back to the previous
* configuration.
* @return the current configuration
*/
public static BMUnitConfigState getCurrentConfigState() {
return currentConfigState;
}
public BMUnitConfigState currentConfigState()
{
return currentConfigState;
}
/**
* getter for currently configured agent host setting
* @return the current host setting
*/
public String getHost() {
// agentHost can be overridden so don't look up previous
return agentHost;
}
/**
* getter for currently configured agent port setting
* @return the current port setting
*/
public int getPort() {
// agentPort can be overridden so don't look up previous
// unlesss current port is 0
if (agentPort == 0 && previous != null) {
return previous.getPort();
}
return agentPort;
}
/**
* getter for currently configured load directory setting
* @return the current load directory setting
*/
public String getLoadDirectory() {
// loadDirectory can be overridden so don't look up previous
// unless it is empty or null
if ((loadDirectory != null || loadDirectory.length() == 0) && previous != null) {
return previous.getLoadDirectory();
}
return loadDirectory;
}
/**
* getter for currently configured resource directory setting
* @return the current resource directory setting
*/
public String getResourceLoadDirectory() {
// loadDirectory can be overridden so don't look up previous
// unless it is empty or null
if ((resourceLoadDirectory != null || resourceLoadDirectory.length() == 0) && previous != null) {
return previous.getResourceLoadDirectory();
}
return resourceLoadDirectory;
}
/**
* smart getter for currently configured allowAgentConfigUpdate setting
* which redirects through to the previous config to ensure that
* we employ the setting used for the initial agent load
* @return the current allowAgentConfigUpdate setting
*/
public boolean isAllowConfigUpdate() {
// allowAgentConfigUpdate cannot be overridden so check for previous setting
if (previous != null) {
return previous.isAllowConfigUpdate();
}
return allowConfigUpdate;
}
/**
* smart getter for currently configured Byteman verbose setting
* which redirects through to the previous config if config
* updates are not allowed but otherwise returns the currently
* configured Byteman verbose setting
* @return the current Byteman verbose setting
*/
public boolean isVerbose() {
// verbose can be overridden if allowAgentConfigUpdate is true
if (previous == null || previous.isAllowConfigUpdate()) {
return verbose;
} else {
return previous.isVerbose();
}
}
/**
* smart getter for currently configured Byteman debug setting
* which redirects through to the previous config if config
* updates are not allowed but otherwise returns the currently
* configured Byteman debug setting
* @return the current Byteman debug setting
*/
public boolean isDebug() {
// debug can be overridden if allowAgentConfigUpdate is true
if (previous == null || previous.isAllowConfigUpdate()) {
return debug;
} else {
return previous.isDebug();
}
}
/**
* getter for currently configured BMUnit verbose setting
* @return the current BMUnit verbose setting
*/
public boolean isBMUnitVerbose() {
// bmunitVerbose can be overridden so don't look up previous
return bmunitVerbose;
}
/**
* smart getter for currently configured inhibitAgentLoad setting
* which redirects through to the previous config to ensure that
* we employ the setting used for the initial agent load
* @return the current inhibitAgentLoad setting
*/
public boolean isInhibitAgentLoad() {
// inhibitAgentLoad cannot be overridden so check for previous setting
if (previous != null) {
return previous.isInhibitAgentLoad();
}
return inhibitAgentLoad;
}
/**
* smart getter for currently configured policy setting
* which redirects through to the previous config to ensure that
* we employ the setting used for the initial agent load
* @return the current policy setting
*/
public boolean isPolicy() {
// isPolicy cannot be overridden so check for previous setting
if (previous != null) {
return previous.isPolicy();
}
return policy;
}
/**
* getter for current dumpGeneratedClasses setting
* @return the current dumpGeneratedClasses setting
*/
public boolean isDumpGeneratedClasses()
{
return dumpGeneratedClasses;
}
/**
* smart getter for current dumpGeneratedClassesDirectory setting
* which only returns a directory when dumpGeneratedClasses is set
* in which case it uses any current setting but delegates to previous
* if no value has been set.
* @return the current dumpGeneratedClasseDirectory setting
*/
public String getDumpGeneratedClassesDirectory()
{
if (!dumpGeneratedClasses) {
return "";
}
if (dumpGeneratedClassesDirectory != null && dumpGeneratedClassesDirectory.length() != 0) {
return dumpGeneratedClassesDirectory;
}
if (previous != null) {
return previous.getDumpGeneratedClassesDirectory();
}
return "";
}
/**
* smart getter for current dumpGeneratedClassesIntermediate setting
* which only returns the attribute setting if dumpGeneratedClasses
* has also been set.
* @return the current dumpGeneratedClassesIntermediate
*/
public boolean isDumpGeneratedClassesIntermediate()
{
if (!dumpGeneratedClasses) {
return false;
}
return dumpGeneratedClassesIntermediate;
}
/**
* return the String configured for the agent host or null if it
* was not configured
* @return the iniital agent host
*/
private static String initHost()
{
String host = System.getProperty(AGENT_HOST);
return (host != null ? host : "");
}
/**
* return the integer port configured for the agent port or 0 if
* it was not configured or was misconfigured
* @return the initial agent port
*/
private static int initPort()
{
String portString = System.getProperty(AGENT_PORT);
return (portString == null ? 0 : Integer.valueOf(portString));
}
/**
* computes the default load directory from system property
* org.jboss.byteman.contrib.bmunit.load.directory or defaults it
* to ""
* @return the initial load directory
*/
private static String initDefaultLoadDirectory()
{
String dir = System.getProperty(LOAD_DIRECTORY);
if (dir == null) {
return "";
}
return dir;
}
/**
* computes the default resource load directory from system
* property
* org.jboss.byteman.contrib.bmunit.resource.load.directory or
* defaults it to the load directory
* @return the initial resource load directory
*/
private static String initDefaultResourceLoadDirectory()
{
String dir = System.getProperty(RESOURCE_LOAD_DIRECTORY);
if (dir == null) {
dir = System.getProperty(LOAD_DIRECTORY);
if (dir == null) {
dir = "";
}
}
int l = dir.length();
if (l > 0 && dir.charAt(l - 1) != '/') {
dir = dir + "/";
}
return dir;
}
private static boolean initVerbose()
{
return System.getProperty(BYTEMAN_VERBOSE) != null;
}
private static boolean initDebug()
{
return System.getProperty(BYTEMAN_DEBUG) != null;
}
private static boolean initBMUnitVerbose()
{
return System.getProperty(BMUNIT_VERBOSE) != null;
}
/**
* test whether a security policy should be set for agent code
* when the agent is installed
* @return the initial policy setting
*/
private static boolean initPolicy()
{
String policyString= System.getProperty(AGENT_POLICY);
return (policyString == null ? false : Boolean.valueOf(policyString));
}
private static boolean initDumpGeneratedClasses()
{
return System.getProperty(BYTEMAN_DUMP_GENERATED_CLASSES) != null;
}
private static String initDumpGeneratedClassesDirectory()
{
String dir = System.getProperty(BYTEMAN_DUMP_GENERATED_CLASSES_DIRECTORY);
if (dir == null) {
dir = "";
}
return dir;
}
private static boolean initDumpGeneratedClassesIntermediate()
{
return System.getProperty(BYTEMAN_DUMP_GENERATED_CLASSES_INTERMEDIATE) != null;
}
}