/*
* Copyright 2013-2015 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 org.springframework.integration.expression;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.json.JsonPathUtils;
import org.springframework.integration.json.TestPerson;
import org.springframework.integration.support.MutableMessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
/**
* @author Gary Russell
* @author Artem Bilan
* @since 3.0
*/
public class ParentContextTests {
private static final List<EvaluationContext> evalContexts = new ArrayList<EvaluationContext>();
/**
* Verifies that beans in hierarchical contexts get an evaluation context that has the proper
* BeanResolver. Verifies that the two Foos in the parent context get an evaluation context
* with the same bean resolver. Verifies that the one Foo in the child context gets a different
* bean resolver. Verifies that bean references in SpEL expressions to beans in the child
* and parent contexts work. Verifies that PropertyAccessors are inherited in the child context
* and the parent's ones are last in the propertyAccessors list of EvaluationContext.
* Verifies that SpEL functions are inherited from parent context and overridden with the same 'id'.
* Verifies that child and parent contexts can have different message builders.
* <p>
* Only single test method is allowed for 'ParentContext-context.xml',
* since it relies on static 'evalContexts' variable.
*/
@Test
@SuppressWarnings("unchecked")
public void testSpelBeanReferencesInChildAndParent() throws Exception {
AbstractApplicationContext parent = new ClassPathXmlApplicationContext("ParentContext-context.xml",
this.getClass());
Object parentEvaluationContextFactoryBean = parent.getBean(IntegrationEvaluationContextFactoryBean.class);
Map<?, ?> parentFunctions = TestUtils.getPropertyValue(parentEvaluationContextFactoryBean, "functions",
Map.class);
assertEquals(4, parentFunctions.size());
Object jsonPath = parentFunctions.get("jsonPath");
assertNotNull(jsonPath);
assertThat((Method) jsonPath, Matchers.isOneOf(JsonPathUtils.class.getMethods()));
assertEquals(2, evalContexts.size());
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(parent);
child.setConfigLocation("org/springframework/integration/expression/ChildContext-context.xml");
child.refresh();
Object childEvaluationContextFactoryBean = child.getBean(IntegrationEvaluationContextFactoryBean.class);
Map<?, ?> childFunctions = TestUtils.getPropertyValue(childEvaluationContextFactoryBean, "functions",
Map.class);
assertEquals(5, childFunctions.size());
assertTrue(childFunctions.containsKey("barParent"));
assertTrue(childFunctions.containsKey("fooFunc"));
jsonPath = childFunctions.get("jsonPath");
assertNotNull(jsonPath);
assertThat((Method) jsonPath, Matchers.not(Matchers.isOneOf(JsonPathUtils.class.getMethods())));
assertEquals(3, evalContexts.size());
assertSame(evalContexts.get(0).getBeanResolver(), evalContexts.get(1).getBeanResolver());
List<PropertyAccessor> propertyAccessors = evalContexts.get(0).getPropertyAccessors();
assertEquals(4, propertyAccessors.size());
PropertyAccessor parentPropertyAccessorOverride = parent.getBean("jsonPropertyAccessor", PropertyAccessor.class);
PropertyAccessor parentPropertyAccessor = parent.getBean("parentJsonPropertyAccessor", PropertyAccessor.class);
assertTrue(propertyAccessors.contains(parentPropertyAccessorOverride));
assertTrue(propertyAccessors.contains(parentPropertyAccessor));
assertTrue(propertyAccessors.indexOf(parentPropertyAccessorOverride) > propertyAccessors.indexOf(parentPropertyAccessor));
Map<String, Object> variables = (Map<String, Object>) TestUtils.getPropertyValue(evalContexts.get(0),
"variables");
assertEquals(4, variables.size());
assertTrue(variables.containsKey("bar"));
assertTrue(variables.containsKey("barParent"));
assertTrue(variables.containsKey("fooFunc"));
assertTrue(variables.containsKey("jsonPath"));
assertNotSame(evalContexts.get(1).getBeanResolver(), evalContexts.get(2).getBeanResolver());
propertyAccessors = evalContexts.get(1).getPropertyAccessors();
assertEquals(4, propertyAccessors.size());
assertTrue(propertyAccessors.contains(parentPropertyAccessorOverride));
variables = (Map<String, Object>) TestUtils.getPropertyValue(evalContexts.get(1), "variables");
assertEquals(4, variables.size());
assertTrue(variables.containsKey("bar"));
assertTrue(variables.containsKey("barParent"));
assertTrue(variables.containsKey("fooFunc"));
assertTrue(variables.containsKey("jsonPath"));
propertyAccessors = evalContexts.get(2).getPropertyAccessors();
assertEquals(4, propertyAccessors.size());
PropertyAccessor childPropertyAccessor = child.getBean("jsonPropertyAccessor", PropertyAccessor.class);
assertTrue(propertyAccessors.contains(childPropertyAccessor));
assertTrue(propertyAccessors.contains(parentPropertyAccessor));
assertFalse(propertyAccessors.contains(parentPropertyAccessorOverride));
assertTrue(propertyAccessors.indexOf(childPropertyAccessor) < propertyAccessors.indexOf(parentPropertyAccessor));
variables = (Map<String, Object>) TestUtils.getPropertyValue(evalContexts.get(2), "variables");
assertEquals(5, variables.size());
assertTrue(variables.containsKey("bar"));
assertTrue(variables.containsKey("barParent"));
assertTrue(variables.containsKey("fooFunc"));
assertTrue(variables.containsKey("barChild"));
assertTrue(variables.containsKey("jsonPath"));
// Test transformer expressions
child.getBean("input", MessageChannel.class).send(new GenericMessage<String>("baz"));
Message<?> out = child.getBean("output", QueueChannel.class).receive(0);
assertNotNull(out);
assertEquals("foobar", out.getPayload());
child.getBean("parentIn", MessageChannel.class).send(MutableMessageBuilder.withPayload("bar").build());
out = child.getBean("parentOut", QueueChannel.class).receive(0);
assertNotNull(out);
assertThat(out, instanceOf(GenericMessage.class));
assertEquals("foo", out.getPayload());
IntegrationEvaluationContextFactoryBean evaluationContextFactoryBean =
child.getBean("&" + IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME,
IntegrationEvaluationContextFactoryBean.class);
try {
evaluationContextFactoryBean.setPropertyAccessors(Collections.<String, PropertyAccessor>emptyMap());
fail("IllegalArgumentException expected.");
}
catch (Exception e) {
assertThat(e, Matchers.instanceOf(IllegalArgumentException.class));
}
parent.getBean("fromParentToChild", MessageChannel.class).send(new GenericMessage<String>("foo"));
out = child.getBean("output", QueueChannel.class).receive(0);
assertNotNull(out);
assertEquals("org.springframework.integration.support.MutableMessage", out.getClass().getName());
assertEquals("FOO", out.getPayload());
assertTrue(parent
.containsBean(IntegrationContextUtils.TO_STRING_FRIENDLY_JSON_NODE_TO_STRING_CONVERTER_BEAN_NAME));
assertTrue(child
.containsBean(IntegrationContextUtils.TO_STRING_FRIENDLY_JSON_NODE_TO_STRING_CONVERTER_BEAN_NAME));
Object converterRegistrar = parent.getBean(IntegrationContextUtils.CONVERTER_REGISTRAR_BEAN_NAME);
assertNotNull(converterRegistrar);
Set<?> converters = TestUtils.getPropertyValue(converterRegistrar, "converters", Set.class);
boolean toStringFriendlyJsonNodeToStringConverterPresent = false;
for (Object converter : converters) {
if ("ToStringFriendlyJsonNodeToStringConverter".equals(converter.getClass().getSimpleName())) {
toStringFriendlyJsonNodeToStringConverterPresent = true;
break;
}
}
assertTrue(toStringFriendlyJsonNodeToStringConverterPresent);
MessageChannel input = parent.getBean("testJsonNodeToStringConverterInputChannel", MessageChannel.class);
PollableChannel output = parent.getBean("testJsonNodeToStringConverterOutputChannel", PollableChannel.class);
TestPerson person = new TestPerson();
person.setFirstName("John");
input.send(new GenericMessage<Object>(person));
Message<?> result = output.receive(1000);
assertNotNull(result);
assertEquals("JOHN", result.getPayload());
child.close();
parent.close();
}
public static class Foo implements BeanFactoryAware, InitializingBean {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
evalContexts.add(ExpressionUtils.createStandardEvaluationContext(this.beanFactory));
}
}
public static class Bar {
public static Object bar(Object o) {
return o;
}
public String testJsonNodeToStringConverter(String payload) {
return payload.toUpperCase();
}
}
}