/* * 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.integration.file.remote; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willReturn; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.BeanFactory; import org.springframework.integration.IntegrationMessageHeaderAccessor; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.file.FileHeaders; import org.springframework.integration.file.filters.AcceptOnceFileListFilter; import org.springframework.integration.file.remote.session.Session; import org.springframework.integration.file.remote.session.SessionFactory; import org.springframework.integration.file.splitter.FileSplitter; import org.springframework.integration.transformer.StreamTransformer; import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; /** * @author Gary Russell * @since 4.3 * */ public class StreamingInboundTests { @Rule public ExpectedException exception = ExpectedException.none(); private final StreamTransformer transformer = new StreamTransformer(); @SuppressWarnings("unchecked") @Test public void testAllData() throws Exception { StringSessionFactory sessionFactory = new StringSessionFactory(); Streamer streamer = new Streamer(new StringRemoteFileTemplate(sessionFactory), null); streamer.setBeanFactory(mock(BeanFactory.class)); streamer.setRemoteDirectory("/foo"); streamer.afterPropertiesSet(); Message<byte[]> received = (Message<byte[]>) this.transformer.transform(streamer.receive()); assertEquals("foo\nbar", new String(received.getPayload())); assertEquals("/foo", received.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("foo", received.getHeaders().get(FileHeaders.REMOTE_FILE)); String fileInfo = (String) received.getHeaders().get(FileHeaders.REMOTE_FILE_INFO); assertThat(fileInfo, containsString("remoteDirectory\":\"/foo")); assertThat(fileInfo, containsString("permissions\":\"-rw-rw-rw")); assertThat(fileInfo, containsString("size\":42")); assertThat(fileInfo, containsString("directory\":false")); assertThat(fileInfo, containsString("filename\":\"foo")); assertThat(fileInfo, containsString("modified\":42000")); assertThat(fileInfo, containsString("link\":false")); // close after list, transform verify(new IntegrationMessageHeaderAccessor(received).getCloseableResource(), times(2)).close(); received = (Message<byte[]>) this.transformer.transform(streamer.receive()); assertEquals("baz\nqux", new String(received.getPayload())); assertEquals("/foo", received.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("bar", received.getHeaders().get(FileHeaders.REMOTE_FILE)); fileInfo = (String) received.getHeaders().get(FileHeaders.REMOTE_FILE_INFO); assertThat(fileInfo, containsString("remoteDirectory\":\"/foo")); assertThat(fileInfo, containsString("permissions\":\"-rw-rw-rw")); assertThat(fileInfo, containsString("size\":42")); assertThat(fileInfo, containsString("directory\":false")); assertThat(fileInfo, containsString("filename\":\"bar")); assertThat(fileInfo, containsString("modified\":42000")); assertThat(fileInfo, containsString("link\":false")); // close after transform verify(new IntegrationMessageHeaderAccessor(received).getCloseableResource(), times(3)).close(); verify(sessionFactory.getSession()).list("/foo"); } @SuppressWarnings("unchecked") @Test public void testAllDataMaxFetch() throws Exception { StringSessionFactory sessionFactory = new StringSessionFactory(); Streamer streamer = new Streamer(new StringRemoteFileTemplate(sessionFactory), null); streamer.setBeanFactory(mock(BeanFactory.class)); streamer.setRemoteDirectory("/foo"); streamer.setMaxFetchSize(1); streamer.setFilter(new AcceptOnceFileListFilter<>()); streamer.afterPropertiesSet(); Message<byte[]> received = (Message<byte[]>) this.transformer.transform(streamer.receive()); assertEquals("foo\nbar", new String(received.getPayload())); assertEquals("/foo", received.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("foo", received.getHeaders().get(FileHeaders.REMOTE_FILE)); // close after list, transform verify(new IntegrationMessageHeaderAccessor(received).getCloseableResource(), times(2)).close(); received = (Message<byte[]>) this.transformer.transform(streamer.receive()); assertEquals("baz\nqux", new String(received.getPayload())); assertEquals("/foo", received.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("bar", received.getHeaders().get(FileHeaders.REMOTE_FILE)); // close after list, transform verify(new IntegrationMessageHeaderAccessor(received).getCloseableResource(), times(4)).close(); verify(sessionFactory.getSession(), times(2)).list("/foo"); } @Test public void testExceptionOnFetch() { exception.expect(MessagingException.class); StringSessionFactory sessionFactory = new StringSessionFactory(); Streamer streamer = new Streamer(new StringRemoteFileTemplate(sessionFactory), null); streamer.setBeanFactory(mock(BeanFactory.class)); streamer.setRemoteDirectory("/bad"); streamer.afterPropertiesSet(); streamer.receive(); } @SuppressWarnings("unchecked") @Test public void testLineByLine() throws Exception { Streamer streamer = new Streamer(new StringRemoteFileTemplate(new StringSessionFactory()), null); streamer.setBeanFactory(mock(BeanFactory.class)); streamer.setRemoteDirectory("/foo"); streamer.afterPropertiesSet(); QueueChannel out = new QueueChannel(); FileSplitter splitter = new FileSplitter(); splitter.setBeanFactory(mock(BeanFactory.class)); splitter.setOutputChannel(out); splitter.afterPropertiesSet(); Message<InputStream> receivedStream = streamer.receive(); splitter.handleMessage(receivedStream); Message<byte[]> received = (Message<byte[]>) out.receive(0); assertEquals("foo", received.getPayload()); assertEquals("/foo", received.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("foo", received.getHeaders().get(FileHeaders.REMOTE_FILE)); received = (Message<byte[]>) out.receive(0); assertEquals("bar", received.getPayload()); assertEquals("/foo", received.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("foo", received.getHeaders().get(FileHeaders.REMOTE_FILE)); assertNull(out.receive(0)); // close by list, splitter verify(new IntegrationMessageHeaderAccessor(receivedStream).getCloseableResource(), times(2)).close(); receivedStream = streamer.receive(); splitter.handleMessage(receivedStream); received = (Message<byte[]>) out.receive(0); assertEquals("baz", received.getPayload()); assertEquals("/foo", received.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("bar", received.getHeaders().get(FileHeaders.REMOTE_FILE)); received = (Message<byte[]>) out.receive(0); assertEquals("qux", received.getPayload()); assertEquals("/foo", received.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("bar", received.getHeaders().get(FileHeaders.REMOTE_FILE)); assertNull(out.receive(0)); // close by splitter verify(new IntegrationMessageHeaderAccessor(receivedStream).getCloseableResource(), times(3)).close(); } public static class Streamer extends AbstractRemoteFileStreamingMessageSource<String> { protected Streamer(RemoteFileTemplate<String> template, Comparator<AbstractFileInfo<String>> comparator) { super(template, comparator); } @Override public String getComponentType() { return "Streamer"; } @Override protected List<AbstractFileInfo<String>> asFileInfoList(Collection<String> files) { List<AbstractFileInfo<String>> infos = new ArrayList<AbstractFileInfo<String>>(); for (String file : files) { infos.add(new StringFileInfo(file)); } return infos; } } public static class StringFileInfo extends AbstractFileInfo<String> { private final String name; private StringFileInfo(String name) { this.name = name; } @Override public boolean isDirectory() { return false; } @Override public boolean isLink() { return false; } @Override public long getSize() { return 42; } @Override public long getModified() { return 42_000; } @Override public String getFilename() { return this.name.substring(this.name.lastIndexOf("/") + 1); } @Override public String getPermissions() { return "-rw-rw-rw"; } @Override public String getFileInfo() { return asString(); } private String asString() { return "StringFileInfo [name=" + this.name + "]"; } } public static class StringRemoteFileTemplate extends RemoteFileTemplate<String> { public StringRemoteFileTemplate(SessionFactory<String> sessionFactory) { super(sessionFactory); } } public static class StringSessionFactory implements SessionFactory<String> { private Session<String> session; @SuppressWarnings("unchecked") @Override public Session<String> getSession() { if (this.session != null) { return this.session; } try { Session<String> session = mock(Session.class); willReturn(new String[] { "/foo/foo", "/foo/bar" }).given(session).list("/foo"); ByteArrayInputStream foo = new ByteArrayInputStream("foo\nbar".getBytes()); ByteArrayInputStream bar = new ByteArrayInputStream("baz\nqux".getBytes()); willReturn(foo).given(session).readRaw("/foo/foo"); willReturn(bar).given(session).readRaw("/foo/bar"); willReturn(new String[] { "/bar/foo", "/bar/bar" }).given(session).list("/bar"); ByteArrayInputStream foo2 = new ByteArrayInputStream("foo\r\nbar".getBytes()); ByteArrayInputStream bar2 = new ByteArrayInputStream("baz\r\nqux".getBytes()); willReturn(foo2).given(session).readRaw("/bar/foo"); willReturn(bar2).given(session).readRaw("/bar/bar"); willReturn(new String[] { "/bad/file" }).given(session).list("/bad"); willThrow(new IOException("No file")).given(session).readRaw("/bad/file"); given(session.finalizeRaw()).willReturn(true); this.session = session; return session; } catch (Exception e) { throw new RuntimeException("failed to mock session", e); } } } }