/*
* Copyright 2006-2010 the original author or authors.
*
* Licensed 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 com.consol.citrus.context;
import com.consol.citrus.TestCase;
import com.consol.citrus.endpoint.EndpointFactory;
import com.consol.citrus.exceptions.CitrusRuntimeException;
import com.consol.citrus.exceptions.VariableNullValueException;
import com.consol.citrus.functions.FunctionRegistry;
import com.consol.citrus.functions.FunctionUtils;
import com.consol.citrus.message.MessageHeaders;
import com.consol.citrus.messaging.Consumer;
import com.consol.citrus.report.TestListeners;
import com.consol.citrus.validation.MessageValidatorRegistry;
import com.consol.citrus.validation.interceptor.MessageConstructionInterceptors;
import com.consol.citrus.validation.matcher.ValidationMatcherRegistry;
import com.consol.citrus.variable.GlobalVariables;
import com.consol.citrus.variable.VariableUtils;
import com.consol.citrus.xml.namespace.NamespaceContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
/**
* Class holding and managing test variables. The test context also provides utility methods
* for replacing dynamic content(variables and functions) in message payloads and headers.
*
* @author Christoph Deppisch
*/
public class TestContext {
/**
* Logger
*/
private static Logger log = LoggerFactory.getLogger(TestContext.class);
/** Local variables */
protected Map<String, Object> variables;
/** Global variables */
private GlobalVariables globalVariables;
/** Function registry holding all available functions */
private FunctionRegistry functionRegistry = new FunctionRegistry();
/** Endpoint factory creates endpoint instances */
private EndpointFactory endpointFactory;
/** Registered message validators */
private MessageValidatorRegistry messageValidatorRegistry = new MessageValidatorRegistry();
/** Registered validation matchers */
private ValidationMatcherRegistry validationMatcherRegistry = new ValidationMatcherRegistry();
/** List of test listeners to be informed on test events */
private TestListeners testListeners = new TestListeners();
/** List of global message construction interceptors */
private MessageConstructionInterceptors messageConstructionInterceptors = new MessageConstructionInterceptors();
/** Central namespace context builder */
private NamespaceContextBuilder namespaceContextBuilder = new NamespaceContextBuilder();
/** Spring bean application context */
private ApplicationContext applicationContext;
/**
* Default constructor
*/
public TestContext() {
variables = new ConcurrentHashMap<String, Object>();
}
/**
* Gets the value for the given variable expression. Expression usually is the
* simple variable name, with optional expression prefix/suffix.
*
* In case variable is not known to the context throw runtime exception.
*
* @param variableExpression expression to search for.
* @throws CitrusRuntimeException
* @return value of the variable
*/
public String getVariable(final String variableExpression) {
return getVariableObject(variableExpression).toString();
}
/**
* Gets the value for the given variable as object representation.
* Use this method if you seek for test objects stored in the context.
*
* @param variableExpression expression to search for.
* @throws CitrusRuntimeException
* @return value of the variable as object
*/
public Object getVariableObject(final String variableExpression) {
String variableName = VariableUtils.cutOffVariablesPrefix(variableExpression);
if (!variables.containsKey(variableName)) {
throw new CitrusRuntimeException("Unknown variable '" + variableName + "'");
}
return variables.get(variableName);
}
/**
* Creates a new variable in this test context with the respective value. In case variable already exists
* variable is overwritten.
*
* @param variableName the name of the new variable
* @param value the new variable value
* @throws CitrusRuntimeException
* @return
*/
public void setVariable(final String variableName, Object value) {
if (!StringUtils.hasText(variableName) || VariableUtils.cutOffVariablesPrefix(variableName).length() == 0) {
throw new CitrusRuntimeException("Can not create variable '"+ variableName + "', please define proper variable name");
}
if (value == null) {
throw new VariableNullValueException("Trying to set variable: " + VariableUtils.cutOffVariablesPrefix(variableName) + ", but variable value is null");
}
if (log.isDebugEnabled()) {
log.debug("Setting variable: " + VariableUtils.cutOffVariablesPrefix(variableName) + " with value: '" + value + "'");
}
variables.put(VariableUtils.cutOffVariablesPrefix(variableName), value);
}
/**
* Add several new variables to test context. Existing variables will be
* overwritten.
*
* @param variablesToSet the list of variables to set.
*/
public void addVariables(Map<String, Object> variablesToSet) {
for (Entry<String, Object> entry : variablesToSet.entrySet()) {
if (entry.getValue() != null) {
setVariable(entry.getKey(), entry.getValue());
} else {
setVariable(entry.getKey(), "");
}
}
}
/**
* Replaces variables and functions inside a map with respective values and returns a new
* map representation.
*
* @param map optionally having variable entries.
* @return the constructed map without variable entries.
*/
public Map<String, Object> resolveDynamicValuesInMap(final Map<String, ?> map) {
Map<String, Object> target = new HashMap<String, Object>(map.size());
for (Entry<String, ?> entry : map.entrySet()) {
String key = entry.getKey();
String value = (String) entry.getValue();
//put value into target map, but check if value is variable or function first
target.put(key, replaceDynamicContentInString(value));
}
return target;
}
/**
* Replaces variables and functions in a list with respective values and
* returns the new list representation.
*
* @param list having optional variable entries.
* @return the constructed list without variable entries.
*/
public List<String> resolveDynamicValuesInList(final List<String> list) {
List<String> variableFreeList = new ArrayList<String>(list.size());
for (String entry : list) {
//add new value after check if it is variable or function
variableFreeList.add(replaceDynamicContentInString(entry));
}
return variableFreeList;
}
/**
* Clears variables in this test context. Initially adds all global variables.
*/
public void clear() {
variables.clear();
variables.putAll(globalVariables.getVariables());
}
/**
* Checks if variables are present right now.
* @return boolean flag to mark existence
*/
public boolean hasVariables() {
return !CollectionUtils.isEmpty(variables);
}
/**
* Method replacing variable declarations and place holders as well as
* function expressions in a string
*
* @param str the string to parse.
* @return resulting string without any variable place holders.
*/
public String replaceDynamicContentInString(String str) {
return replaceDynamicContentInString(str, false);
}
/**
* Method replacing variable declarations and functions in a string, optionally
* the variable values get surrounded with single quotes.
*
* @param str the string to parse for variable place holders.
* @param enableQuoting flag marking surrounding quotes should be added or not.
* @return resulting string without any variable place holders.
*/
public String replaceDynamicContentInString(final String str, boolean enableQuoting) {
String result;
result = VariableUtils.replaceVariablesInString(str, this, enableQuoting);
result = FunctionUtils.replaceFunctionsInString(result, this, enableQuoting);
return result;
}
/**
* Checks wether the given expression is a variable or function and resolves the value
* accordingly
* @param expression the expression to resolve
* @return the resolved expression value
*/
public String resolveDynamicValue(String expression) {
if (VariableUtils.isVariableName(expression)) {
return getVariable(expression);
} else if (functionRegistry.isFunction(expression)) {
return FunctionUtils.resolveFunction(expression, this);
}
return expression;
}
/**
* Handles error creating a new CitrusRuntimeException and
* informs test listeners.
*
* @param testName
* @param packageName
* @param message
* @param cause
* @return
*/
public CitrusRuntimeException handleError(String testName, String packageName, String message, Exception cause) {
// Create empty dummy test case for logging purpose
TestCase dummyTest = new TestCase();
dummyTest.setName(testName);
dummyTest.setPackageName(packageName);
CitrusRuntimeException exception = new CitrusRuntimeException(message, cause);
// inform test listeners with failed test
testListeners.onTestStart(dummyTest);
testListeners.onTestFailure(dummyTest, exception);
testListeners.onTestFinish(dummyTest);
return exception;
}
/**
* Setter for test variables in this context.
* @param variables
*/
public void setVariables(Map<String, Object> variables) {
this.variables = variables;
}
/**
* Getter for test variables in this context.
* @return test variables for this test context.
*/
public Map<String, Object> getVariables() {
return variables;
}
/**
* Get global variables.
* @param globalVariables
*/
public void setGlobalVariables(GlobalVariables globalVariables) {
this.globalVariables = globalVariables;
variables.putAll(globalVariables.getVariables());
}
/**
* Set global variables.
* @return the globalVariables
*/
public Map<String, Object> getGlobalVariables() {
return globalVariables.getVariables();
}
/**
* Get the current function registry.
* @return the functionRegistry
*/
public FunctionRegistry getFunctionRegistry() {
return functionRegistry;
}
/**
* Set the function registry.
* @param functionRegistry the functionRegistry to set
*/
public void setFunctionRegistry(FunctionRegistry functionRegistry) {
this.functionRegistry = functionRegistry;
}
/**
* Set the message validator registry.
* @param messageValidatorRegistry the messageValidatorRegistry to set
*/
public void setMessageValidatorRegistry(MessageValidatorRegistry messageValidatorRegistry) {
this.messageValidatorRegistry = messageValidatorRegistry;
}
/**
* Get the message validator registry.
* @return the messageValidatorRegistry
*/
public MessageValidatorRegistry getMessageValidatorRegistry() {
return messageValidatorRegistry;
}
/**
* Get the current validation matcher registry
* @return
*/
public ValidationMatcherRegistry getValidationMatcherRegistry() {
return validationMatcherRegistry;
}
/**
* Set the validation matcher registry
* @param validationMatcherRegistry
*/
public void setValidationMatcherRegistry(ValidationMatcherRegistry validationMatcherRegistry) {
this.validationMatcherRegistry = validationMatcherRegistry;
}
/**
* Gets the test listeners.
* @return
*/
public TestListeners getTestListeners() {
return testListeners;
}
/**
* Set the test listeners.
* @param testListeners
*/
public void setTestListeners(TestListeners testListeners) {
this.testListeners = testListeners;
}
/**
* Gets the message construction interceptors.
* @return
*/
public MessageConstructionInterceptors getMessageConstructionInterceptors() {
return messageConstructionInterceptors;
}
/**
* Sets the messsage construction interceptors.
* @param messageConstructionInterceptors
*/
public void setMessageConstructionInterceptors(MessageConstructionInterceptors messageConstructionInterceptors) {
this.messageConstructionInterceptors = messageConstructionInterceptors;
}
/**
* Gets the endpoint factory.
* @return
*/
public EndpointFactory getEndpointFactory() {
return endpointFactory;
}
/**
* Sets the endpoint factory.
* @param endpointFactory
*/
public void setEndpointFactory(EndpointFactory endpointFactory) {
this.endpointFactory = endpointFactory;
}
/**
* Sets the namespace context builder.
* @param namespaceContextBuilder
*/
public void setNamespaceContextBuilder(NamespaceContextBuilder namespaceContextBuilder) {
this.namespaceContextBuilder = namespaceContextBuilder;
}
/**
* Gets the namespace context builder.
* @return
*/
public NamespaceContextBuilder getNamespaceContextBuilder() {
return namespaceContextBuilder;
}
/**
* Gets the Spring bean application context.
* @return
*/
public ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* Sets the Spring bean application context.
* @param applicationContext
*/
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Gets special correlation key test variable for given consumer instance.
* @param consumer
* @return
*/
public String getCorrelationKey(Consumer consumer) {
if (log.isDebugEnabled()) {
log.debug(String.format("Get correlation key for '%s'", MessageHeaders.MESSAGE_CORRELATION_KEY + "_" + consumer.getName()));
}
if (variables.containsKey(MessageHeaders.MESSAGE_CORRELATION_KEY + "_" + consumer.getName())) {
return getVariable(MessageHeaders.MESSAGE_CORRELATION_KEY + "_" + consumer.getName());
}
throw new CitrusRuntimeException(String.format("Failed to get correlation key for '%s'", MessageHeaders.MESSAGE_CORRELATION_KEY + "_" + consumer.getName()));
}
/**
* Saves new correlation key for given consumer instance.
* @param correlationKey
* @param consumer
*/
public void saveCorrelationKey(String correlationKey, Consumer consumer) {
if (log.isDebugEnabled()) {
log.debug(String.format("Saving correlation key for '%s'", MessageHeaders.MESSAGE_CORRELATION_KEY + "_" + consumer.getName()));
}
setVariable(MessageHeaders.MESSAGE_CORRELATION_KEY + "_" + consumer.getName(), correlationKey);
}
}