/*
* Copyright 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.cloud.stream.module.syslog.source;
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.assertThat;
import static org.junit.Assert.assertTrue;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.net.SocketFactory;
import org.hamcrest.Matchers;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.cloud.stream.modules.test.PropertiesInitializer;
import org.springframework.cloud.stream.test.binder.MessageCollector;
import org.springframework.context.ApplicationContext;
import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNetServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNioServerConnectionFactory;
import org.springframework.integration.ip.udp.UnicastReceivingChannelAdapter;
import org.springframework.integration.syslog.inbound.UdpSyslogReceivingChannelAdapter;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Tests for SyslogSource.
*
* @author Gary Russell
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SyslogSourceApplication.class, initializers = PropertiesInitializer.class)
@DirtiesContext
@IntegrationTest("port = 0")
public abstract class SyslogSourceTests {
protected static final String RFC3164_PACKET = "<157>JUL 26 22:08:35 WEBERN TESTING[70729]: TEST SYSLOG MESSAGE";
protected static final String RFC5424_PACKET =
"<14>1 2014-06-20T09:14:07+00:00 loggregator d0602076-b14a-4c55-852a-981e7afeed38 DEA - "
+ "[exampleSDID@32473 iut=\\\"3\\\" eventSource=\\\"Application\\\" eventID=\\\"1011\\\"]"
+ "[exampleSDID@32473 iut=\\\"3\\\" eventSource=\\\"Application\\\" eventID=\\\"1011\\\"] "
+ "Removing instance";
@Autowired
protected Source channels;
@Autowired
protected MessageCollector messageCollector;
@Autowired(required = false)
protected AbstractServerConnectionFactory connectionFactory;
@Autowired(required = false)
protected UdpSyslogReceivingChannelAdapter udpAdapter;
@Autowired
protected SyslogSourceProperties properties;
@Autowired
protected ApplicationContext context;
@Value("${port}")
private int port;
@BeforeClass
public static void configureSource() throws Throwable {
// TODO: we can remove this when SI 4.3 is used; UDP can then specify port=0, TCP can already do it.
DatagramSocket socket = new DatagramSocket(0);
int port = socket.getLocalPort();
socket.close();
Properties properties = new Properties();
properties.put("port", Integer.toString(port));
PropertiesInitializer.PROPERTIES = properties;
}
@IntegrationTest({ "port = 0", "nio = true", "reverseLookup = true",
"socketTimeout = 123", "bufferSize = 5" })
public static class PropertiesPopulatedTests extends SyslogSourceTests {
@Test
public void test() throws Exception {
assertThat(this.connectionFactory, Matchers.instanceOf(TcpNioServerConnectionFactory.class));
assertTrue(TestUtils.getPropertyValue(this.connectionFactory, "lookupHost", Boolean.class));
assertEquals(123, TestUtils.getPropertyValue(this.connectionFactory, "soTimeout"));
assertEquals(5, TestUtils.getPropertyValue(this.connectionFactory, "deserializer.maxMessageSize"));
}
}
public static class NotNioTests extends SyslogSourceTests {
@Test
public void test() throws Exception {
assertThat(this.connectionFactory, Matchers.instanceOf(TcpNetServerConnectionFactory.class));
assertFalse(TestUtils.getPropertyValue(this.connectionFactory, "lookupHost", Boolean.class));
assertEquals(0, TestUtils.getPropertyValue(this.connectionFactory, "soTimeout"));
assertEquals(2048, TestUtils.getPropertyValue(this.connectionFactory, "deserializer.maxMessageSize"));
}
}
public static class Tcp3164Tests extends SyslogSourceTests {
@Test
public void test() throws Exception {
sendTcp(SyslogSourceTests.RFC3164_PACKET + "\n");
Message<?> syslog = messageCollector.forChannel(channels.output()).poll(10, TimeUnit.SECONDS);
assertNotNull(syslog);
assertThat(syslog.getPayload(), instanceOf(Map.class));
assertEquals("WEBERN", ((Map<?, ?>) syslog.getPayload()).get("HOST"));
}
}
@IntegrationTest({ "port = 0", "rfc = 5424" })
public static class Tcp5424Tests extends SyslogSourceTests {
@Test
public void test() throws Exception {
sendTcp("253 " + RFC5424_PACKET);
Message<?> syslog = messageCollector.forChannel(channels.output()).poll(10, TimeUnit.SECONDS);
assertNotNull(syslog);
assertThat(syslog.getPayload(), instanceOf(Map.class));
assertEquals("loggregator", ((Map<?, ?>) syslog.getPayload()).get("syslog_HOST"));
}
}
@IntegrationTest("protocol = udp")
public static class Udp3164Tests extends SyslogSourceTests {
@Test
public void test() throws Exception {
sendUdp(RFC3164_PACKET);
Message<?> syslog = messageCollector.forChannel(channels.output()).poll(10, TimeUnit.SECONDS);
assertNotNull(syslog);
assertThat(syslog.getPayload(), instanceOf(Map.class));
assertEquals("WEBERN", ((Map<?, ?>) syslog.getPayload()).get("HOST"));
}
}
@IntegrationTest({ "protocol = udp", "rfc = 5424" })
public static class Udp5424Tests extends SyslogSourceTests {
@Test
public void test() throws Exception {
sendUdp(RFC5424_PACKET);
Message<?> syslog = messageCollector.forChannel(channels.output()).poll(10, TimeUnit.SECONDS);
assertNotNull(syslog);
assertThat(syslog.getPayload(), instanceOf(Map.class));
assertEquals("loggregator", ((Map<?, ?>) syslog.getPayload()).get("syslog_HOST"));
}
}
@IntegrationTest("protocol = both")
public static class TcpAndUdp3164Tests extends SyslogSourceTests {
@Test
public void test() throws Exception {
sendTcp(SyslogSourceTests.RFC3164_PACKET + "\n");
Message<?> syslog = messageCollector.forChannel(channels.output()).poll(10, TimeUnit.SECONDS);
assertNotNull(syslog);
assertThat(syslog.getPayload(), instanceOf(Map.class));
assertEquals("WEBERN", ((Map<?, ?>) syslog.getPayload()).get("HOST"));
sendUdp(RFC3164_PACKET);
syslog = messageCollector.forChannel(channels.output()).poll(10, TimeUnit.SECONDS);
assertNotNull(syslog);
assertThat(syslog.getPayload(), instanceOf(Map.class));
assertEquals("WEBERN", ((Map<?, ?>) syslog.getPayload()).get("HOST"));
}
}
@IntegrationTest({ "protocol = both", "rfc = 5424" })
public static class TcpAndUdp5424Tests extends SyslogSourceTests {
@Test
public void test() throws Exception {
sendTcp("253 " + RFC5424_PACKET);
Message<?> syslog = messageCollector.forChannel(channels.output()).poll(10, TimeUnit.SECONDS);
assertNotNull(syslog);
assertThat(syslog.getPayload(), instanceOf(Map.class));
assertEquals("loggregator", ((Map<?, ?>) syslog.getPayload()).get("syslog_HOST"));
sendUdp(RFC5424_PACKET);
syslog = messageCollector.forChannel(channels.output()).poll(10, TimeUnit.SECONDS);
assertNotNull(syslog);
assertThat(syslog.getPayload(), instanceOf(Map.class));
assertEquals("loggregator", ((Map<?, ?>) syslog.getPayload()).get("syslog_HOST"));
}
}
protected void sendTcp(String syslog) throws Exception {
int port = getPort();
Socket socket = SocketFactory.getDefault().createSocket("localhost", port);
socket.getOutputStream().write(syslog.getBytes());
socket.close();
}
protected void sendUdp(String syslog) throws Exception {
waitUdp();
DatagramSocket socket = new DatagramSocket();
DatagramPacket packet = new DatagramPacket(syslog.getBytes(), syslog.length());
packet.setSocketAddress(new InetSocketAddress("localhost", this.port));
socket.send(packet);
socket.close();
}
private int getPort() throws Exception {
int n = 0;
while (n++ < 100 && !this.connectionFactory.isListening()) {
Thread.sleep(100);
}
assertTrue("server failed to start listening", this.connectionFactory.isListening());
int port = this.connectionFactory.getPort();
assertTrue("server stopped listening", port > 0);
return port;
}
private void waitUdp() throws Exception {
int n = 0;
DirectFieldAccessor dfa = new DirectFieldAccessor(this.udpAdapter);
while (n++ < 100 && !((UnicastReceivingChannelAdapter) dfa.getPropertyValue("udpAdapter")).isListening()) {
Thread.sleep(100);
}
}
}