/* * Copyright 2002-2016 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.gateway; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import java.lang.reflect.Method; import java.util.Collections; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.junit.Test; import org.mockito.Mockito; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.expression.Expression; import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.support.utils.IntegrationUtils; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.support.GenericMessage; import org.springframework.util.ReflectionUtils; /** * @author Mark Fisher * @author Oleg Zhurakousky * @author Gunnar Hillert * @author Gary Russell */ public class GatewayProxyFactoryBeanTests { @Test public void testRequestReplyWithAnonymousChannel() throws Exception { QueueChannel requestChannel = new QueueChannel(); startResponder(requestChannel); GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); proxyFactory.setDefaultRequestChannel(requestChannel); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.setBeanName("testGateway"); proxyFactory.afterPropertiesSet(); TestService service = (TestService) proxyFactory.getObject(); String result = service.requestReply("foo"); assertEquals("foobar", result); } @Test public void testRequestReplyWithAnonymousChannelConvertedTypeViaConversionService() throws Exception { QueueChannel requestChannel = new QueueChannel(); startResponder(requestChannel); GenericConversionService cs = new DefaultConversionService(); Converter<String, byte[]> stringToByteConverter = new Converter<String, byte[]>() { @Override public byte[] convert(String source) { return source.getBytes(); } }; stringToByteConverter = spy(stringToByteConverter); cs.addConverter(stringToByteConverter); GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.registerSingleton(IntegrationUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME, cs); proxyFactory.setBeanFactory(bf); proxyFactory.setDefaultRequestChannel(requestChannel); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setBeanName("testGateway"); proxyFactory.afterPropertiesSet(); TestService service = (TestService) proxyFactory.getObject(); byte[] result = service.requestReplyInBytes("foo"); assertEquals(6, result.length); Mockito.verify(stringToByteConverter, Mockito.times(1)).convert(Mockito.any(String.class)); } @Test public void testOneWay() throws Exception { final QueueChannel requestChannel = new QueueChannel(); GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setDefaultRequestChannel(requestChannel); proxyFactory.setBeanName("testGateway"); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.afterPropertiesSet(); TestService service = (TestService) proxyFactory.getObject(); service.oneWay("test"); Message<?> message = requestChannel.receive(1000); assertNotNull(message); assertEquals("test", message.getPayload()); } @Test public void testSolicitResponse() throws Exception { QueueChannel replyChannel = new QueueChannel(); replyChannel.send(new GenericMessage<String>("foo")); GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setDefaultRequestChannel(new DirectChannel()); proxyFactory.setDefaultReplyChannel(replyChannel); proxyFactory.setBeanName("testGateway"); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.afterPropertiesSet(); TestService service = (TestService) proxyFactory.getObject(); String result = service.solicitResponse(); assertNotNull(result); assertEquals("foo", result); } @Test public void testReceiveMessage() throws Exception { QueueChannel replyChannel = new QueueChannel(); replyChannel.send(new GenericMessage<>("foo")); GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setDefaultReplyChannel(replyChannel); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.afterPropertiesSet(); TestService service = (TestService) proxyFactory.getObject(); Message<String> message = service.getMessage(); assertNotNull(message); assertEquals("foo", message.getPayload()); } @Test public void testRequestReplyWithTypeConversion() throws Exception { final QueueChannel requestChannel = new QueueChannel(); new Thread(() -> { Message<?> input = requestChannel.receive(); GenericMessage<String> reply = new GenericMessage<String>(input.getPayload() + "456"); ((MessageChannel) input.getHeaders().getReplyChannel()).send(reply); }).start(); GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setDefaultRequestChannel(requestChannel); proxyFactory.setBeanName("testGateway"); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.afterPropertiesSet(); TestService service = (TestService) proxyFactory.getObject(); Integer result = service.requestReplyWithIntegers(123); assertEquals(new Integer(123456), result); } @Test public void testRequestReplyWithRendezvousChannelInApplicationContext() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "gatewayWithRendezvousChannel.xml", GatewayProxyFactoryBeanTests.class); TestService service = (TestService) context.getBean("proxy"); String result = service.requestReply("foo"); assertEquals("foo!!!", result); context.close(); } @Test public void testRequestReplyWithResponseCorrelatorInApplicationContext() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "gatewayWithResponseCorrelator.xml", GatewayProxyFactoryBeanTests.class); TestService service = (TestService) context.getBean("proxy"); String result = service.requestReply("foo"); assertEquals("foo!!!", result); TestChannelInterceptor interceptor = (TestChannelInterceptor) context.getBean("interceptor"); assertEquals(1, interceptor.getSentCount()); assertEquals(1, interceptor.getReceivedCount()); context.close(); } @Test public void testMultipleMessagesWithResponseCorrelator() throws InterruptedException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "gatewayWithResponseCorrelator.xml", GatewayProxyFactoryBeanTests.class); int numRequests = 500; final TestService service = (TestService) context.getBean("proxy"); final String[] results = new String[numRequests]; final CountDownLatch latch = new CountDownLatch(numRequests); Executor executor = Executors.newFixedThreadPool(numRequests); for (int i = 0; i < numRequests; i++) { final int count = i; executor.execute(() -> { // add some randomness to the ordering of requests try { Thread.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { // ignore } results[count] = service.requestReply("test-" + count); latch.countDown(); }); } latch.await(30, TimeUnit.SECONDS); for (int i = 0; i < numRequests; i++) { assertEquals("test-" + i + "!!!", results[i]); } TestChannelInterceptor interceptor = (TestChannelInterceptor) context.getBean("interceptor"); assertEquals(numRequests, interceptor.getSentCount()); assertEquals(numRequests, interceptor.getReceivedCount()); context.close(); } @Test public void testMessageAsMethodArgument() throws Exception { QueueChannel requestChannel = new QueueChannel(); startResponder(requestChannel); GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setDefaultRequestChannel(requestChannel); proxyFactory.setBeanName("testGateway"); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.afterPropertiesSet(); TestService service = (TestService) proxyFactory.getObject(); String result = service.requestReplyWithMessageParameter(new GenericMessage<String>("foo")); assertEquals("foobar", result); } @Test public void testNoArgMethodWithPayloadAnnotation() throws Exception { QueueChannel requestChannel = new QueueChannel(); startResponder(requestChannel); GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setDefaultRequestChannel(requestChannel); proxyFactory.setBeanName("testGateway"); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.afterPropertiesSet(); TestService service = (TestService) proxyFactory.getObject(); String result = service.requestReplyWithPayloadAnnotation(); assertEquals("requestReplyWithPayloadAnnotation0bar", result); } @Test public void testMessageAsReturnValue() throws Exception { final QueueChannel requestChannel = new QueueChannel(); new Thread(() -> { Message<?> input = requestChannel.receive(); GenericMessage<String> reply = new GenericMessage<String>(input.getPayload() + "bar"); ((MessageChannel) input.getHeaders().getReplyChannel()).send(reply); }).start(); GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setDefaultRequestChannel(requestChannel); proxyFactory.setBeanName("testGateway"); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.afterPropertiesSet(); TestService service = (TestService) proxyFactory.getObject(); Message<?> result = service.requestReplyWithMessageReturnValue("foo"); assertEquals("foobar", result.getPayload()); } @Test public void testServiceMustBeInterface() { GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); int count = 0; try { proxyFactory.setServiceInterface(TestService.class); count++; proxyFactory.setServiceInterface(String.class); count++; } catch (IllegalArgumentException e) { // expected } assertEquals(1, count); } @Test public void testProxiedToStringMethod() throws Exception { GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); proxyFactory.setDefaultRequestChannel(new DirectChannel()); proxyFactory.setServiceInterface(TestService.class); proxyFactory.setBeanName("testGateway"); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.afterPropertiesSet(); Object proxy = proxyFactory.getObject(); String expected = "gateway proxy for"; assertEquals(expected, proxy.toString().substring(0, expected.length())); } @Test(expected = TestException.class) public void testCheckedExceptionRethrownAsIs() throws Exception { GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); DirectChannel channel = new DirectChannel(); EventDrivenConsumer consumer = new EventDrivenConsumer(channel, new MessageHandler() { @Override public void handleMessage(Message<?> message) { Method method = ReflectionUtils.findMethod( GatewayProxyFactoryBeanTests.class, "throwTestException"); ReflectionUtils.invokeMethod(method, this); } }); consumer.start(); proxyFactory.setDefaultRequestChannel(channel); proxyFactory.setServiceInterface(TestExceptionThrowingInterface.class); proxyFactory.setBeanName("testGateway"); proxyFactory.setBeanFactory(mock(BeanFactory.class)); proxyFactory.afterPropertiesSet(); TestExceptionThrowingInterface proxy = (TestExceptionThrowingInterface) proxyFactory.getObject(); proxy.throwCheckedException("test"); } private static void startResponder(final PollableChannel requestChannel) { new Thread(() -> { Message<?> input = requestChannel.receive(); GenericMessage<String> reply = new GenericMessage<String>(input.getPayload() + "bar"); ((MessageChannel) input.getHeaders().getReplyChannel()).send(reply); }).start(); } @Test public void testProgrammaticWiring() throws Exception { GatewayProxyFactoryBean gpfb = new GatewayProxyFactoryBean(); gpfb.setBeanFactory(mock(BeanFactory.class)); gpfb.setServiceInterface(TestEchoService.class); QueueChannel drc = new QueueChannel(); gpfb.setDefaultRequestChannel(drc); gpfb.setDefaultReplyTimeout(0L); GatewayMethodMetadata meta = new GatewayMethodMetadata(); meta.setHeaderExpressions(Collections.<String, Expression>singletonMap("foo", new LiteralExpression("bar"))); gpfb.setGlobalMethodMetadata(meta); gpfb.afterPropertiesSet(); ((TestEchoService) gpfb.getObject()).echo("foo"); Message<?> message = drc.receive(0); assertNotNull(message); String bar = (String) message.getHeaders().get("foo"); assertNotNull(bar); assertThat(bar, equalTo("bar")); } // @Test // public void testHistory() throws Exception { // GenericApplicationContext context = new GenericApplicationContext(); // context.getBeanFactory().registerSingleton("historyWriter", new MessageHistoryWriter()); // GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean(); // proxyFactory.setBeanFactory(context); // proxyFactory.setBeanName("testGateway"); // DirectChannel channel = new DirectChannel(); // channel.setBeanName("testChannel"); // channel.setBeanFactory(context); // channel.afterPropertiesSet(); // BridgeHandler bridgeHandler = new BridgeHandler(); // bridgeHandler.setBeanFactory(context); // bridgeHandler.afterPropertiesSet(); // bridgeHandler.setBeanName("testBridge"); // EventDrivenConsumer consumer = new EventDrivenConsumer(channel, bridgeHandler); // consumer.setBeanFactory(context); // consumer.afterPropertiesSet(); // consumer.start(); // proxyFactory.setDefaultRequestChannel(channel); // proxyFactory.setServiceInterface(TestEchoService.class); // proxyFactory.afterPropertiesSet(); // TestEchoService proxy = (TestEchoService) proxyFactory.getObject(); // Message<?> message = proxy.echo("test"); // Iterator<MessageHistoryEvent> historyIterator = message.getHeaders().getHistory().iterator(); // MessageHistoryEvent event1 = historyIterator.next(); // MessageHistoryEvent event2 = historyIterator.next(); // MessageHistoryEvent event3 = historyIterator.next(); // // //assertEquals("echo", event1.getAttribute("method", String.class)); // assertEquals("gateway", event1.getType()); // assertEquals("testGateway", event1.getName()); // assertEquals("channel", event2.getType()); // assertEquals("testChannel", event2.getName()); // assertEquals("bridge", event3.getType()); // assertEquals("testBridge", event3.getName()); // } @Test public void autowiredGateway() { new ClassPathXmlApplicationContext("gatewayAutowiring.xml", GatewayProxyFactoryBeanTests.class).close(); } public static void throwTestException() throws TestException { throw new TestException(); } interface TestEchoService { Message<?> echo(String s); } interface TestExceptionThrowingInterface { String throwCheckedException(String s) throws TestException; } @SuppressWarnings("serial") static class TestException extends Exception { } public static class TestClient { @SuppressWarnings("unused") private final TestService service; @Autowired public TestClient(TestService service) { this.service = service; } } }