/* * 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.xd.dirt.stream; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Method; import java.util.Iterator; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.io.FileUtils; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.core.env.PropertiesPropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.integration.file.FileHeaders; import org.springframework.integration.file.splitter.FileSplitter; import org.springframework.integration.file.splitter.FileSplitter.FileMarker; import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; import org.springframework.messaging.converter.ContentTypeResolver; import org.springframework.messaging.support.GenericMessage; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.MethodCallback; import org.springframework.util.ReflectionUtils.MethodFilter; import org.springframework.xd.dirt.integration.bus.StringConvertingContentTypeResolver; import org.springframework.xd.dirt.plugins.ModuleConfigurationException; /** * * @author David Turanski * @author Gunnar Hillert * @author Gary Russell */ public class FileSourceModuleTests extends StreamTestSupport { private static String tmpDirName = System.getProperty("java.io.tmpdir"); private static String sourceDirName = tmpDirName + (tmpDirName.endsWith(File.separator) ? "" : File.separator) + "filesourcetests"; private static File sourceDir = new File(sourceDirName); ContentTypeResolver contentTypeResolver = new StringConvertingContentTypeResolver(); @BeforeClass public static void createTempDir() throws IOException { FileUtils.forceMkdir(sourceDir); } @Before public void setUp() throws IOException { if (sourceDir.exists()) { FileUtils.cleanDirectory(sourceDir); } } @Test public void testFileContents() throws IOException { deployStream( "filecontents", "file --mode=contents --dir=" + sourceDirName + " --fixedDelay=0 | sink"); MessageTest test = new MessageTest() { @Override public void test(Message<?> message) throws MessagingException { byte[] bytes = (byte[]) message.getPayload(); assertEquals("foo", new String(bytes)); assertEquals("foo.txt", message.getHeaders().get(FileHeaders.FILENAME, String.class)); assertEquals(MimeType.valueOf("application/octet-stream"), contentTypeResolver.resolve(message.getHeaders())); } }; StreamTestSupport.getSinkInputChannel("filecontents").subscribe(test); dropFile("foo.txt"); test.waitForCompletion(1000); undeployStream("filecontents"); assertTrue(test.getMessageHandled()); } @Test public void testFileContentsUpperCase() throws IOException { deployStream( "filecontentsuppercase", "file --mode=CONTENTS --dir=" + sourceDirName + " --fixedDelay=0 | sink"); MessageTest test = new MessageTest() { @Override public void test(Message<?> message) throws MessagingException { byte[] bytes = (byte[]) message.getPayload(); assertEquals("foo", new String(bytes)); assertEquals("foo.txt", message.getHeaders().get(FileHeaders.FILENAME, String.class)); assertEquals(MimeType.valueOf("application/octet-stream"), contentTypeResolver.resolve(message.getHeaders())); } }; StreamTestSupport.getSinkInputChannel("filecontentsuppercase").subscribe(test); dropFile("foo.txt"); test.waitForCompletion(3000); undeployStream("filecontentsuppercase"); assertTrue(test.getMessageHandled()); } @Test public void testFileContentsUsingDefaultMode() throws IOException { deployStream( "filecontentsdefault", "file --dir=" + sourceDirName + " --fixedDelay=0 | sink"); MessageTest test = new MessageTest() { @Override public void test(Message<?> message) throws MessagingException { byte[] bytes = (byte[]) message.getPayload(); assertEquals("foo", new String(bytes)); assertEquals("foo.txt", message.getHeaders().get(FileHeaders.FILENAME, String.class)); assertEquals(MimeType.valueOf("application/octet-stream"), contentTypeResolver.resolve(message.getHeaders())); } }; StreamTestSupport.getSinkInputChannel("filecontentsdefault").subscribe(test); dropFile("foo.txt"); test.waitForCompletion(1000); undeployStream("filecontentsdefault"); assertTrue(test.getMessageHandled()); } @Test public void testFileContentsAsString() throws IOException { deployStream( "filestring", "file --dir=" + sourceDirName + " --fixedDelay=0 --outputType='text/plain;charset=UTF-8' | sink"); MessageTest test = new MessageTest() { @Override public void test(Message<?> message) throws MessagingException { assertEquals("foo", message.getPayload()); assertEquals("foo2.txt", message.getHeaders().get(FileHeaders.FILENAME, String.class)); assertEquals(MimeTypeUtils.APPLICATION_OCTET_STREAM, contentTypeResolver.resolve(message.getHeaders())); } }; StreamTestSupport.getSinkInputChannel("filestring").subscribe(test); dropFile("foo2.txt"); test.waitForCompletion(6000); StreamTestSupport.getDeployedModule("filestring", 0).stop(); assertTrue(test.getMessageHandled()); } @Test public void testFileReference() throws IOException { deployStream( "fileref", "file --mode=ref --dir=" + sourceDirName + " --fixedDelay=0 | sink"); MessageTest test = new MessageTest() { @Override public void test(Message<?> message) throws MessagingException { File file = (File) message.getPayload(); assertEquals(sourceDirName + File.separator + "foo1.txt", file.getAbsolutePath()); } }; StreamTestSupport.getSinkInputChannel("fileref").subscribe(test); dropFile("foo1.txt"); test.waitForCompletion(1000); StreamTestSupport.getDeployedModule("fileref", 0).stop(); assertTrue(test.getMessageHandled()); } @Test public void testLinesMode() throws IOException { deployStream( "textLine", "file --mode=lines --dir=" + sourceDirName + " --fixedDelay=0 | sink"); MessageTest test = new MessageTest() { private AtomicInteger counter = new AtomicInteger(0); @Override public void test(Message<?> message) throws MessagingException { assertTrue("Extected a String", message.getPayload() instanceof String); assertEquals("foo", message.getPayload()); assertEquals("foo1.txt", message.getHeaders().get(FileHeaders.FILENAME, String.class)); counter.incrementAndGet(); } @Override public boolean getMessageHandled() { if (counter.get() == 10) { super.messageHandled = true; } return super.messageHandled; } }; StreamTestSupport.getSinkInputChannel("textLine").subscribe(test); dropFile("foo1.txt", 10); test.waitForCompletion(3000); undeployStream("textLine"); assertTrue(test.getMessageHandled()); } @Test public void testLinesModeWithMarkersTrue() throws IOException { deployStream( "textLineWithEmitMarkersTrue", "file --mode=lines --withMarkers=true --dir=" + sourceDirName + " --fixedDelay=0 | sink"); MessageTest test = new MessageTest() { private AtomicInteger counter = new AtomicInteger(0); private FileMarker startFileMarker = null; private FileMarker endFileMarker = null; @Override public void test(Message<?> message) throws MessagingException { if (message.getPayload() instanceof FileMarker) { final FileMarker fileMarker = (FileMarker) message.getPayload(); if (FileMarker.Mark.START.equals(fileMarker.getMark())) { startFileMarker = fileMarker; } else if (FileMarker.Mark.END.equals(fileMarker.getMark())) { endFileMarker = fileMarker; } } else { assertTrue("Extected a String", message.getPayload() instanceof String); assertEquals("foo", message.getPayload()); assertEquals("foo1.txt", message.getHeaders().get(FileHeaders.FILENAME, String.class)); counter.incrementAndGet(); } } @Override public boolean getMessageHandled() { if (counter.get() == 10 && startFileMarker != null && endFileMarker != null) { super.messageHandled = true; } return super.messageHandled; } }; StreamTestSupport.getSinkInputChannel("textLineWithEmitMarkersTrue").subscribe(test); dropFile("foo1.txt", 10); test.waitForCompletion(3000); undeployStream("textLineWithEmitMarkersTrue"); assertTrue(test.getMessageHandled()); } @Test public void testRefModeWithWithMarkersTrue() throws IOException { try { deployStream( "refWithMarkersTrue", "file --mode=ref --withMarkers=true --dir=" + sourceDirName + " --fixedDelay=0 | sink"); } catch (ModuleConfigurationException e) { String expectation = "withMarkers can only be supplied when FileReadingMode is 'lines'"; assertTrue("Expected the exception to contain: " + expectation, e.getMessage().contains(expectation)); return; } fail("Was expecting a 'ModuleConfigurationException' to be thrown."); } @Test public void testRefModeWithWithMarkersFalse() throws IOException { try { deployStream( "refWithMarkersTrue", "file --mode=ref --withMarkers=false --dir=" + sourceDirName + " --fixedDelay=0 | sink"); } catch (ModuleConfigurationException e) { String expectation = "withMarkers can only be supplied when FileReadingMode is 'lines'"; assertTrue("Expected the exception to contain: " + expectation, e.getMessage().contains(expectation)); return; } fail("Was expecting a 'ModuleConfigurationException' to be thrown."); } @Test public void testTextLineModeWithIncorrectMode() throws IOException { try { deployStream( "failme", "file --mode=failme --dir=" + sourceDirName + " --fixedDelay=0 | sink"); } catch (ModuleConfigurationException e) { String expectation = "Failed to convert property value of type [java.lang.String] to required type " + "[org.springframework.xd.dirt.modules.metadata.FileReadingMode] for property 'mode'"; assertThat(e.getMessage(), containsString(expectation)); return; } fail("Was expecting a 'ModuleConfigurationException' to be thrown."); } @Test public void testSplitterUsesIterator() throws Exception { System.out.println(System.getProperty("user.dir")); ConfigurableApplicationContext ctx = new FileSystemXmlApplicationContext(new String[] { "../modules/common/file-source-common-context.xml", "classpath:org/springframework/xd/dirt/stream/ppc-context.xml" }, false); StandardEnvironment env = new StandardEnvironment(); Properties props = new Properties(); props.setProperty("fixedDelay", "5"); props.setProperty("timeUnit", "SECONDS"); props.setProperty("initialDelay", "0"); props.setProperty("withMarkers", "false"); PropertiesPropertySource pps = new PropertiesPropertySource("props", props); env.getPropertySources().addLast(pps); env.setActiveProfiles("use-contents-with-split"); ctx.setEnvironment(env); ctx.refresh(); FileSplitter splitter = ctx.getBean(FileSplitter.class); File foo = File.createTempFile("foo", ".txt"); final AtomicReference<Method> splitMessage = new AtomicReference<>(); ReflectionUtils.doWithMethods(FileSplitter.class, new MethodCallback() { @Override public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { method.setAccessible(true); splitMessage.set(method); } }, new MethodFilter() { @Override public boolean matches(Method method) { return method.getName().equals("splitMessage"); } }); Object result = splitMessage.get().invoke(splitter, new GenericMessage<File>(foo)); assertThat(result, instanceOf(Iterator.class)); ctx.close(); foo.delete(); } private void dropFile(String fileName) throws IOException { dropFile(fileName, 1); } private void dropFile(String fileName, int lines) throws IOException { PrintWriter writer = new PrintWriter(sourceDirName + "/" + fileName, "UTF-8"); for (int i = 1; i <= lines; i++) { writer.write("foo"); if (i < lines) { writer.write("\n"); } } writer.close(); } @AfterClass public static void deleteTempDir() throws IOException { if (sourceDir.exists()) { FileUtils.cleanDirectory(sourceDir); FileUtils.deleteDirectory(sourceDir); } } }