/** * Copyright (c) Codice Foundation * <p/> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p/> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package ddf.content.endpoint.directorymonitor; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.camel.Exchange; import org.apache.camel.builder.AdviceWithRouteBuilder; import org.apache.camel.component.mock.MockComponent; import org.apache.camel.model.FromDefinition; import org.apache.camel.model.ModelCamelContext; import org.apache.camel.model.ProcessorDefinition; import org.apache.camel.model.RouteDefinition; import org.apache.camel.model.SetHeaderDefinition; import org.apache.camel.test.junit4.CamelTestSupport; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ddf.content.core.directorymonitor.ContentDirectoryMonitor; public class ContentDirectoryMonitorTest extends CamelTestSupport { private static final Logger LOGGER = LoggerFactory.getLogger(ContentDirectoryMonitorTest.class); private static final String INPUT_FILENAME = "input.txt"; private static final String INPUT_FILEPATH = "target/" + INPUT_FILENAME; private ModelCamelContext camelContext; private ContentDirectoryMonitor contentDirectoryMonitor; @After public void tearDown() throws Exception { LOGGER.debug("INSIDE tearDown"); //context = null; // This will also stop all routes/components/endpoints, etc. // and clear internal state/cache camelContext.stop(); camelContext = null; } @Test public void testRouteCreationWithCopyIngestedFiles() throws Exception { String monitoredDirectory = "target/inbox"; String directive = "PROCESS"; boolean copyIngestedFiles = true; RouteDefinition routeDefinition = createRoute(monitoredDirectory, directive, copyIngestedFiles); verifyRoute(routeDefinition, monitoredDirectory, directive, copyIngestedFiles); } @Test public void testRouteCreationWithoutCopyIngestedFiles() throws Exception { String monitoredDirectory = "target/inbox"; String directive = "PROCESS"; boolean copyIngestedFiles = false; RouteDefinition routeDefinition = createRoute(monitoredDirectory, directive, copyIngestedFiles); verifyRoute(routeDefinition, monitoredDirectory, directive, copyIngestedFiles); } @Test public void testRouteCreationMissingMonitoredDirectory() throws Exception { String monitoredDirectory = ""; String directive = "PROCESS"; boolean copyIngestedFiles = true; camelContext = (ModelCamelContext) super.createCamelContext(); camelContext.start(); // Map the "content" scheme to a mock component so that we do not have to // mock the entire custom ContentComponent and include its implementation // in pom with scope=test camelContext.addComponent("content", new MockComponent()); contentDirectoryMonitor = new ContentDirectoryMonitor(camelContext); contentDirectoryMonitor.setMonitoredDirectoryPath(monitoredDirectory); contentDirectoryMonitor.setDirective(directive); contentDirectoryMonitor.setCopyIngestedFiles(copyIngestedFiles); // Simulates what container would do once all setters have been invoked contentDirectoryMonitor.init(); assertThat(camelContext.getRouteDefinitions().size(), is(0)); } @Test public void testRouteCreationMissingDirective() throws Exception { String monitoredDirectory = "target/inbox"; String directive = ""; boolean copyIngestedFiles = true; camelContext = (ModelCamelContext) super.createCamelContext(); camelContext.start(); // Map the "content" scheme to a mock component so that we do not have to // mock the entire custom ContentComponent and include its implementation // in pom with scope=test camelContext.addComponent("content", new MockComponent()); contentDirectoryMonitor = new ContentDirectoryMonitor(camelContext); contentDirectoryMonitor.setMonitoredDirectoryPath(monitoredDirectory); contentDirectoryMonitor.setDirective(directive); contentDirectoryMonitor.setCopyIngestedFiles(copyIngestedFiles); // Simulates what container would do once all setters have been invoked contentDirectoryMonitor.init(); assertThat(camelContext.getRouteDefinitions().size(), is(0)); } @Test public void testMoveFolder() throws Exception { String monitoredDirectory = "target/inbox"; String directive = "PROCESS"; boolean copyIngestedFiles = true; RouteDefinition routeDefinition = createRoute(monitoredDirectory, directive, copyIngestedFiles); // Put file in monitored directory String fileContents = "Dummy data in a text file"; FileUtils.writeStringToFile(new File(INPUT_FILEPATH), fileContents); template.sendBodyAndHeader("file://" + monitoredDirectory, fileContents, Exchange.FILE_NAME, INPUT_FILENAME); Thread.sleep(3000); // Verify the file moved to the .ingested directory File target = new File(monitoredDirectory + "/.ingested/" + INPUT_FILENAME); assertTrue("File not moved to .ingested folder", target.exists()); // Cleanup FileUtils.deleteDirectory(new File(monitoredDirectory)); } /** * Verify if route has a failure then the file being processed is moved to the .errors * directory. * * @throws Exception */ /* * TODO: how to fail the route * * @Test public void testMoveFailedFolder() throws Exception { String monitoredDirectory = * "target/inbox"; String directive = "PROCESS"; boolean copyIngestedFiles = true; * * //RouteDefinition routeDefinition = createRoute(monitoredDirectory, directive, * copyIngestedFiles); CamelContext camelContext = super.createCamelContext(); * * MimeTypeMapper mimeTypeMapper = mock(MimeTypeMapper.class); * when(mimeTypeMapper.getMimeTypeForFileExtension("txt")).thenReturn(null); * * ContentItem contentItem = mock(ContentItem.class); * when(contentItem.getId()).thenReturn("123"); * when(contentItem.toString()).thenReturn("contentItem toString output"); * * CreateResponse createResponse = mock(CreateResponse.class); * when(createResponse.getCreatedContentItem()).thenReturn(contentItem); * * ContentFramework contentFramework = mock(ContentFramework.class); * when(contentFramework.create(isA(CreateRequest.class), * isA(Request.Directive.class))).thenReturn(createResponse); * * ContentComponent contentComponent = new ContentComponent(); * contentComponent.setMimeTypeMapper(mimeTypeMapper); * camelContext.addComponent(ContentComponent.NAME, contentComponent); * * ContentDirectoryMonitor contentDirectoryMonitor = new ContentDirectoryMonitor(camelContext); * contentDirectoryMonitor.setMonitoredDirectoryPath(monitoredDirectory); * contentDirectoryMonitor.setDirective(directive); * contentDirectoryMonitor.setCopyIngestedFiles(copyIngestedFiles); * * // Simulates what container would do once all setters have been invoked * contentDirectoryMonitor.init(); * * // Initial Camel route should now be created List<RouteDefinition> routeDefinitions = * contentDirectoryMonitor.getRouteDefinitions(); assertThat(routeDefinitions.size(), is(1)); * LOGGER.debug("routeDefinition = " + routeDefinitions.get(0).toString()); * * * // Put file in monitored directory String fileContents = "Dummy data in a text file"; File * file = writeTextFile(INPUT_FILEPATH, fileContents); template.sendBodyAndHeader("file://" + * monitoredDirectory, fileContents, Exchange.FILE_NAME, INPUT_FILENAME); * * Thread.sleep(3000); * * // Verify the file moved to the .errors directory File target = new File(monitoredDirectory + * "/.errors/" + INPUT_FILENAME); assertTrue("File not moved to .errors folder", * target.exists()); * * // Cleanup FileUtils.deleteDirectory(new File(monitoredDirectory)); * camelContext.removeRouteDefinition(routeDefinition); } END TODO */ @Test // @Ignore public void testUpdateExistingDirectoryMonitor() throws Exception { String monitoredDirectory = "target/inbox"; String directive = "PROCESS"; boolean copyIngestedFiles = true; RouteDefinition routeDefinition = createRoute(monitoredDirectory, directive, copyIngestedFiles); // Put file in monitored directory String fileContents = "Dummy data in a text file"; FileUtils.writeStringToFile(new File(INPUT_FILEPATH), fileContents); template.sendBodyAndHeader("file://" + monitoredDirectory, fileContents, Exchange.FILE_NAME, INPUT_FILENAME); Thread.sleep(3000); // Verify the file moved to the .ingested directory File target = new File(monitoredDirectory + "/.ingested/" + INPUT_FILENAME); assertTrue("File 1 not moved to .ingested folder", target.exists()); // Update the existing directory monitor to point to different directory String newMonitoredDirectory = "target/inbox_2"; Map<String, Object> properties = new HashMap<String, Object>(); properties.put("monitoredDirectoryPath", newMonitoredDirectory); properties.put("directive", directive); properties.put("copyIngestedFiles", true); contentDirectoryMonitor.updateCallback(properties); // Put file in new monitored directory fileContents = "Dummy data in second text file"; FileUtils.writeStringToFile(new File("target/input_2.txt"), fileContents); template.sendBodyAndHeader("file://" + newMonitoredDirectory, fileContents, Exchange.FILE_NAME, "input_2.txt"); Thread.sleep(3000); // Verify the file moved to the .ingested directory target = new File(newMonitoredDirectory + "/.ingested/input_2.txt"); assertTrue("File 2 not moved to .ingested folder", target.exists()); // Put file in original monitored directory fileContents = "Dummy data in third text file"; FileUtils.writeStringToFile(new File("target/input_3.txt"), fileContents); template.sendBodyAndHeader("file://" + monitoredDirectory, fileContents, Exchange.FILE_NAME, "input_3.txt"); Thread.sleep(3000); // Verify the file is not moved to the .ingested directory since it is // no longer monitored target = new File(monitoredDirectory + "/.ingested/input_3.txt"); assertFalse("File 3 moved to .ingested folder", target.exists()); target = new File(monitoredDirectory + "/input_3.txt"); assertTrue("File 3 not in old monitored folder", target.exists()); // Cleanup FileUtils.deleteDirectory(new File(monitoredDirectory)); FileUtils.deleteDirectory(new File(newMonitoredDirectory)); } @Test public void testMultipleDirectoryMonitors() throws Exception { String firstMonitoredDirectory = "target/inbox_1"; String directive = "PROCESS"; boolean copyIngestedFiles = true; RouteDefinition firstRouteDefinition = createRoute(firstMonitoredDirectory, directive, copyIngestedFiles); String secondMonitoredDirectory = "target/inbox_2"; directive = "STORE"; copyIngestedFiles = true; RouteDefinition secondRouteDefinition = createRoute(secondMonitoredDirectory, directive, copyIngestedFiles); // Put file in first monitored directory String fileContents = "text file 1"; FileUtils.writeStringToFile(new File(INPUT_FILEPATH), fileContents); template.sendBodyAndHeader("file://" + firstMonitoredDirectory, fileContents, Exchange.FILE_NAME, INPUT_FILENAME); fileContents = "text file 2"; FileUtils.writeStringToFile(new File("target/input_2.txt"), fileContents); template.sendBodyAndHeader("file://" + secondMonitoredDirectory, fileContents, Exchange.FILE_NAME, "input_2.txt"); Thread.sleep(3000); // Verify the files were moved to the correct .ingested directories File target = new File(firstMonitoredDirectory + "/.ingested/" + INPUT_FILENAME); assertTrue("File 1 not moved to .ingested folder", target.exists()); target = new File(secondMonitoredDirectory + "/.ingested/input_2.txt"); assertTrue("File 2 not moved to .ingested folder", target.exists()); // Cleanup FileUtils.deleteDirectory(new File(firstMonitoredDirectory)); FileUtils.deleteDirectory(new File(secondMonitoredDirectory)); } /** * ******************************************************************************** */ private RouteDefinition createRouteWithAdvice(String monitoredDirectory, String directive, boolean copyIngestedFiles) throws Exception { camelContext = (ModelCamelContext) super.createCamelContext(); camelContext.start(); contentDirectoryMonitor = new ContentDirectoryMonitor(camelContext); contentDirectoryMonitor.setMonitoredDirectoryPath(monitoredDirectory); contentDirectoryMonitor.setDirective(directive); contentDirectoryMonitor.setCopyIngestedFiles(copyIngestedFiles); // Simulates what container would do once all setters have been invoked contentDirectoryMonitor.init(); // Did not work because it expects the route to be "adviced" already exists. So the above // init() // call created the initial route with the "content:framework" node in it, and then this // AdviceWithRouteBuilder replaced the content:framework with mock:result, but created a // second // route that had this change in it. (So still get NPE because first route fires and cannot // resolve the "content" scheme) camelContext.getRouteDefinitions().get(0) .adviceWith(camelContext, new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { // weave the node in the route which has id = content://framework // and replace it with the following route path weaveByToString(".*content:framework.*").replace().to("mock:result"); } }); // Initial Camel route should now be created List<RouteDefinition> routeDefinitions = contentDirectoryMonitor.getRouteDefinitions(); assertThat(routeDefinitions.size(), is(1)); LOGGER.debug("routeDefinition = {}", routeDefinitions.get(0)); return routeDefinitions.get(0); } private RouteDefinition createRoute(String monitoredDirectory, String directive, boolean copyIngestedFiles) throws Exception { // Simulates what container would do for <camel:camelContext id="camelContext"> // declaration in beans.xml file camelContext = (ModelCamelContext) super.createCamelContext(); camelContext.start(); // Map the "content" scheme to a mock component so that we do not have to // mock the entire custom ContentComponent and include its implementation // in pom with scope=test camelContext.addComponent("content", new MockComponent()); // Otherwise would have to mock all calls the ContentComponent uses // and include the camel-content dependency with scope=test // MimeTypeMapper mimeTypeMapper = mock(MimeTypeMapper.class); // when(mimeTypeMapper.getMimeTypeForFileExtension(".txt")).thenReturn("text/xml"); // // ContentItem contentItem = mock(ContentItem.class); // when(contentItem.getId()).thenReturn("123"); // when(contentItem.toString()).thenReturn("contentItem toString output"); // // CreateResponse createResponse = mock(CreateResponse.class); // when(createResponse.getCreatedContentItem()).thenReturn(contentItem); // // ContentFramework contentFramework = mock(ContentFramework.class); // when(contentFramework.create(isA(CreateRequest.class), // isA(Request.Directive.class))).thenReturn(createResponse); // // ContentComponent contentComponent = new ContentComponent(); // contentComponent.setMimeTypeMapper(mimeTypeMapper); // camelContext.addComponent(ContentComponent.NAME, contentComponent); contentDirectoryMonitor = new ContentDirectoryMonitor(camelContext); contentDirectoryMonitor.setMonitoredDirectoryPath(monitoredDirectory); contentDirectoryMonitor.setDirective(directive); contentDirectoryMonitor.setCopyIngestedFiles(copyIngestedFiles); // Simulates what container would do once all setters have been invoked contentDirectoryMonitor.init(); // Initial Camel route should now be created List<RouteDefinition> routeDefinitions = contentDirectoryMonitor.getRouteDefinitions(); assertThat(routeDefinitions.size(), is(1)); LOGGER.debug("routeDefinition = {}", routeDefinitions.get(0)); return routeDefinitions.get(0); } private void verifyRoute(RouteDefinition routeDefinition, String monitoredDirectory, String directive, boolean copyIngestedFiles) { List<FromDefinition> fromDefinitions = routeDefinition.getInputs(); assertThat(fromDefinitions.size(), is(1)); String uri = fromDefinitions.get(0).getUri(); LOGGER.debug("uri = {}", uri); String expectedUri = "file:" + monitoredDirectory + "?moveFailed=.errors"; if (copyIngestedFiles) { expectedUri += "&move=.ingested"; } else { expectedUri += "&delete=true"; } assertThat(uri, equalTo(expectedUri)); List<ProcessorDefinition<?>> processorDefinitions = routeDefinition.getOutputs(); // expect 4 outputs: SetHeader(operation), SetHeader(directive), SetHeader(contentUri), // To(content:framework) assertThat(processorDefinitions.size(), is(4)); ProcessorDefinition<?> pd = processorDefinitions.get(0); LOGGER.debug("ProcessorDefinition: {}", pd); assertTrue(pd instanceof SetHeaderDefinition); SetHeaderDefinition shd = (SetHeaderDefinition) pd; assertThat(shd.getHeaderName(), equalTo("operation")); // TODO: how to get the values of the SetHeaderDefinition objects // Expression expression = shd.getExpression().getExpressionValue(); // assertThat(shd.getExpression().getExpression(), equalTo("create")); } }