/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.processors.flume;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.flume.sink.NullSink;
import org.apache.flume.source.AvroSource;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.MockProcessContext;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.apache.nifi.util.file.FileUtils;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ExecuteFlumeSourceTest {
private static final Logger logger = LoggerFactory.getLogger(ExecuteFlumeSourceTest.class);
@Rule
public final TemporaryFolder temp = new TemporaryFolder();
@Test
public void testValidators() {
TestRunner runner = TestRunners.newTestRunner(ExecuteFlumeSource.class);
Collection<ValidationResult> results;
ProcessContext pc;
results = new HashSet<>();
runner.enqueue(new byte[0]);
pc = runner.getProcessContext();
if (pc instanceof MockProcessContext) {
results = ((MockProcessContext) pc).validate();
}
Assert.assertEquals(1, results.size());
for (ValidationResult vr : results) {
logger.debug(vr.toString());
Assert.assertTrue(vr.toString().contains("is invalid because Source Type is required"));
}
// non-existent class
results = new HashSet<>();
runner.setProperty(ExecuteFlumeSource.SOURCE_TYPE, "invalid.class.name");
runner.enqueue(new byte[0]);
pc = runner.getProcessContext();
if (pc instanceof MockProcessContext) {
results = ((MockProcessContext) pc).validate();
}
Assert.assertEquals(1, results.size());
for (ValidationResult vr : results) {
logger.debug(vr.toString());
Assert.assertTrue(vr.toString().contains("is invalid because unable to load source"));
}
// class doesn't implement Source
results = new HashSet<>();
runner.setProperty(ExecuteFlumeSource.SOURCE_TYPE, NullSink.class.getName());
runner.enqueue(new byte[0]);
pc = runner.getProcessContext();
if (pc instanceof MockProcessContext) {
results = ((MockProcessContext) pc).validate();
}
Assert.assertEquals(1, results.size());
for (ValidationResult vr : results) {
logger.debug(vr.toString());
Assert.assertTrue(vr.toString().contains("is invalid because unable to create source"));
}
results = new HashSet<>();
runner.setProperty(ExecuteFlumeSource.SOURCE_TYPE, AvroSource.class.getName());
runner.enqueue(new byte[0]);
pc = runner.getProcessContext();
if (pc instanceof MockProcessContext) {
results = ((MockProcessContext) pc).validate();
}
Assert.assertEquals(0, results.size());
}
@Test
public void testSequenceSource() {
TestRunner runner = TestRunners.newTestRunner(ExecuteFlumeSource.class);
runner.setProperty(ExecuteFlumeSource.SOURCE_TYPE, "seq");
runner.run();
List<MockFlowFile> flowFiles = runner.getFlowFilesForRelationship(ExecuteFlumeSource.SUCCESS);
Assert.assertEquals(1, flowFiles.size());
for (MockFlowFile flowFile : flowFiles) {
logger.debug(flowFile.toString());
Assert.assertEquals(1, flowFile.getSize());
}
}
@Test
@Ignore("Does not work on Windows")
public void testSourceWithConfig() throws IOException {
File spoolDirectory = temp.newFolder("spooldir");
File dst = new File(spoolDirectory, "records.txt");
FileUtils.copyFile(getClass().getResourceAsStream("/testdata/records.txt"), dst, true, false);
TestRunner runner = TestRunners.newTestRunner(ExecuteFlumeSource.class);
runner.setProperty(ExecuteFlumeSource.SOURCE_TYPE, "spooldir");
runner.setProperty(ExecuteFlumeSink.FLUME_CONFIG,
"tier1.sources.src-1.spoolDir = " + spoolDirectory.getAbsolutePath());
runner.run(1, false, true);
// Because the spool directory source is an event driven source, it may take some time for flow files to get
// produced. I'm willing to wait up to 5 seconds, but will bail out early if possible. If it takes longer than
// that then there is likely a bug.
int numWaits = 10;
while (runner.getFlowFilesForRelationship(ExecuteFlumeSource.SUCCESS).size() < 4 && --numWaits > 0) {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException ex) {
logger.warn("Sleep interrupted");
}
}
runner.shutdown();
runner.assertTransferCount(ExecuteFlumeSource.SUCCESS, 4);
int i = 1;
for (MockFlowFile flowFile : runner.getFlowFilesForRelationship(ExecuteFlumeSource.SUCCESS)) {
flowFile.assertContentEquals("record " + i);
i++;
}
}
}