/* * Copyright 2002-2017 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.ftp.outbound; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.List; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.logging.Log; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.file.remote.FileInfo; import org.springframework.integration.file.remote.RemoteFileTemplate; import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler; import org.springframework.integration.ftp.session.AbstractFtpSessionFactory; import org.springframework.integration.support.MessageBuilder; 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; import org.springframework.util.FileCopyUtils; /** * @author Oleg Zhurakousky * @author Artem Bilan * @author Gunnar Hillert * @author Gary Russell */ public class FtpOutboundTests { private static FTPClient ftpClient; private TestFtpSessionFactory sessionFactory; @Before public void prepare() { ftpClient = mock(FTPClient.class); sessionFactory = new TestFtpSessionFactory(); sessionFactory.setUsername("kermit"); sessionFactory.setPassword("frog"); sessionFactory.setHost("foo.com"); //sessionFactory.setRemoteWorkingDirectory("remote-test-dir"); } @Test public void testHandleFileContentMessage() throws Exception { File file = new File("remote-target-dir/handlerContent.test"); if (file.exists()) { file.delete(); } assertFalse(file.exists()); FileTransferringMessageHandler<FTPFile> handler = new FileTransferringMessageHandler<FTPFile>(sessionFactory); handler.setRemoteDirectoryExpression(new LiteralExpression("remote-target-dir")); handler.setFileNameGenerator(message -> "handlerContent.test"); handler.setBeanFactory(mock(BeanFactory.class)); handler.afterPropertiesSet(); handler.handleMessage(new GenericMessage<String>("String data")); assertTrue(file.exists()); byte[] inFile = FileCopyUtils.copyToByteArray(file); assertEquals("String data", new String(inFile)); file.delete(); } @Test public void testHandleFileAsByte() throws Exception { File file = new File("remote-target-dir/handlerContent.test"); if (file.exists()) { file.delete(); } assertFalse(file.exists()); FileTransferringMessageHandler<FTPFile> handler = new FileTransferringMessageHandler<FTPFile>(sessionFactory); handler.setRemoteDirectoryExpression(new LiteralExpression("remote-target-dir")); handler.setFileNameGenerator(message -> "handlerContent.test"); handler.setBeanFactory(mock(BeanFactory.class)); handler.afterPropertiesSet(); handler.handleMessage(new GenericMessage<byte[]>("byte[] data".getBytes())); assertTrue(file.exists()); byte[] inFile = FileCopyUtils.copyToByteArray(file); assertEquals("byte[] data", new String(inFile)); file.delete(); } @Test public void testHandleFileMessage() throws Exception { File targetDir = new File("remote-target-dir"); assertTrue("target directory does not exist: " + targetDir.getName(), targetDir.exists()); FileTransferringMessageHandler<FTPFile> handler = new FileTransferringMessageHandler<FTPFile>(sessionFactory); handler.setRemoteDirectoryExpression(new LiteralExpression(targetDir.getName())); handler.setFileNameGenerator(message -> ((File) message.getPayload()).getName() + ".test"); handler.setBeanFactory(mock(BeanFactory.class)); handler.afterPropertiesSet(); File srcFile = File.createTempFile("testHandleFileMessage", ".tmp"); srcFile.deleteOnExit(); File destFile = new File(targetDir, srcFile.getName() + ".test"); destFile.deleteOnExit(); handler.handleMessage(new GenericMessage<File>(srcFile)); assertTrue("destination file was not created", destFile.exists()); } @Test public void testHandleMissingFileMessage() throws Exception { File targetDir = new File("remote-target-dir"); assertTrue("target directory does not exist: " + targetDir.getName(), targetDir.exists()); FileTransferringMessageHandler<FTPFile> handler = new FileTransferringMessageHandler<FTPFile>(sessionFactory); handler.setRemoteDirectoryExpression(new LiteralExpression(targetDir.getName())); handler.setFileNameGenerator(message -> ((File) message.getPayload()).getName() + ".test"); handler.setBeanFactory(mock(BeanFactory.class)); handler.afterPropertiesSet(); File srcFile = new File(UUID.randomUUID() + ".txt"); Log logger = spy(TestUtils.getPropertyValue(handler, "remoteFileTemplate.logger", Log.class)); when(logger.isWarnEnabled()).thenReturn(true); final AtomicReference<String> logged = new AtomicReference<>(); doAnswer(invocation -> { logged.set(invocation.getArgument(0)); invocation.callRealMethod(); return null; }).when(logger).warn(Mockito.anyString()); RemoteFileTemplate<?> template = TestUtils.getPropertyValue(handler, "remoteFileTemplate", RemoteFileTemplate.class); new DirectFieldAccessor(template).setPropertyValue("logger", logger); handler.handleMessage(new GenericMessage<File>(srcFile)); assertNotNull(logged.get()); assertEquals("File " + srcFile.toString() + " does not exist", logged.get()); } @Test //INT-2275 public void testFtpOutboundChannelAdapterInsideChain() throws Exception { File targetDir = new File("remote-target-dir"); assertTrue("target directory does not exist: " + targetDir.getName(), targetDir.exists()); File srcFile = File.createTempFile("testHandleFileMessage", ".tmp"); srcFile.deleteOnExit(); File destFile = new File(targetDir, srcFile.getName()); destFile.deleteOnExit(); ConfigurableApplicationContext context = new ClassPathXmlApplicationContext( "FtpOutboundInsideChainTests-context.xml", getClass()); MessageChannel channel = context.getBean("outboundChainChannel", MessageChannel.class); channel.send(new GenericMessage<File>(srcFile)); assertTrue("destination file was not created", destFile.exists()); context.close(); } @Test //INT-2275 public void testFtpOutboundGatewayInsideChain() throws Exception { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext( "FtpOutboundInsideChainTests-context.xml", getClass()); MessageChannel channel = context.getBean("ftpOutboundGatewayInsideChain", MessageChannel.class); channel.send(MessageBuilder.withPayload("remote-test-dir").build()); PollableChannel output = context.getBean("output", PollableChannel.class); Message<?> result = output.receive(); Object payload = result.getPayload(); assertTrue(payload instanceof List<?>); @SuppressWarnings("unchecked") List<? extends FileInfo<?>> remoteFiles = (List<? extends FileInfo<?>>) payload; assertEquals(3, remoteFiles.size()); List<String> files = Arrays.asList(new File("remote-test-dir").list()); for (FileInfo<?> remoteFile : remoteFiles) { assertTrue(files.contains(remoteFile.getFilename())); } context.close(); } public static class TestFtpSessionFactory extends AbstractFtpSessionFactory<FTPClient> { @Override protected FTPClient createClientInstance() { try { when(ftpClient.getReplyCode()).thenReturn(250); when(ftpClient.login("kermit", "frog")).thenReturn(true); when(ftpClient.changeWorkingDirectory(Mockito.anyString())).thenReturn(true); when(ftpClient.printWorkingDirectory()).thenReturn("remote-target-dir"); when(ftpClient.storeFile(Mockito.anyString(), any(InputStream.class))).thenAnswer(invocation -> { String fileName = invocation.getArgument(0); InputStream fis = invocation.getArgument(1); FileCopyUtils.copy(fis, new FileOutputStream(fileName)); return true; }); when(ftpClient.rename(Mockito.anyString(), Mockito.anyString())).thenAnswer(invocation -> { File file = new File((String) invocation.getArgument(0)); File renameToFile = new File((String) invocation.getArgument(1)); file.renameTo(renameToFile); return true; }); String[] files = new File("remote-test-dir").list(); Collection<FTPFile> ftpFiles = new ArrayList<>(); for (String fileName : files) { FTPFile file = new FTPFile(); file.setName(fileName); file.setType(FTPFile.FILE_TYPE); file.setTimestamp(Calendar.getInstance()); ftpFiles.add(file); when(ftpClient.retrieveFile(Mockito.eq("remote-test-dir/" + fileName), any(OutputStream.class))).thenReturn(true); } when(ftpClient.listFiles("remote-test-dir/")) .thenReturn(ftpFiles.toArray(new FTPFile[ftpFiles.size()])); return ftpClient; } catch (Exception e) { throw new RuntimeException("Failed to create mock client", e); } } } }