/**
* Licensed to Cloudera, Inc. under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Cloudera, Inc. 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 com.cloudera.flume.core.connector;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import org.junit.Test;
import com.cloudera.flume.conf.Context;
import com.cloudera.flume.conf.ReportTestingContext;
import com.cloudera.flume.core.Event;
import com.cloudera.flume.core.EventImpl;
import com.cloudera.flume.core.EventSink;
import com.cloudera.flume.core.EventSource;
import com.cloudera.flume.handlers.debug.InsistentAppendDecorator;
import com.cloudera.flume.handlers.debug.InsistentOpenDecorator;
import com.cloudera.flume.handlers.debug.LazyOpenDecorator;
import com.cloudera.flume.handlers.debug.LazyOpenSource;
import com.cloudera.flume.handlers.debug.NoNlASCIISynthSource;
import com.cloudera.flume.handlers.debug.StubbornAppendSink;
import com.cloudera.flume.handlers.rolling.RollSink;
import com.cloudera.flume.reporter.ReportEvent;
import com.cloudera.util.BackoffPolicy;
import com.cloudera.util.CappedExponentialBackoff;
import com.cloudera.util.Clock;
/**
* This tests the open, close, execution, shutdown and cancellation sematnics of
* the direct driver
*/
public class TestDirectDriver {
/**
* Test a thread cancel on something blocked on open. This forces a
* InterruptedException throw. Normally an insistentOpen will never return if
* the subsink's open always fails. cancel() forces the interrupt flag
* (InterruptedException on blocked things) which the insistent open
* translates into a IOException.
*
* (Ideally it should propagate the InterruptedException, but I think that
* change is pervasive and will wait for the next major version)
*/
@Test
public void testInsistentOpenCancel() throws IOException,
InterruptedException {
EventSink fail4eva = mock(EventSink.Base.class);
doThrow(new IOException("mock exception")).when(fail4eva).open();
doReturn(new ReportEvent("stub")).when(fail4eva).getMetrics();
// max 5s, backoff initially at 10ms
BackoffPolicy bop = new CappedExponentialBackoff(10, 5000);
final InsistentOpenDecorator<EventSink> insistent = new InsistentOpenDecorator<EventSink>(
fail4eva, bop);
final EventSink sink = new LazyOpenDecorator<EventSink>(insistent);
sink.open();
// create an endless stream of data
final EventSource source = new LazyOpenSource<EventSource>(
new NoNlASCIISynthSource(0, 100));
source.open();
DirectDriver driver = new DirectDriver(source, sink);
driver.start();
Clock.sleep(1000); // let the insistent open try a few times.
driver.stop();
boolean closed = driver.join(1000);
assertFalse(closed);
driver.cancel();
assertTrue(driver.join(1000)); // closed this time.
}
/**
* Test a thread cancel on something blocked on append. This forces a
* InterruptedException throw. Normally an insistentAppend will never return
* if the subsink's open always fails. cancel() forces the interrupt flag
* (InterruptedException on blocked things) which the insistent append
* translates into a IOException.
*
* (Ideally it should propagate the InterruptedException, but I think that
* change is pervasive and will wait for the next major version)
*/
@Test
public void testInsistentAppendCancel() throws IOException,
InterruptedException {
EventSink fail4eva = mock(EventSink.Base.class);
final Event e = new EventImpl("foo".getBytes());
doThrow(new IOException("mock exception")).when(fail4eva).append(e);
doReturn(new ReportEvent("mock report")).when(fail4eva).getMetrics();
doReturn("mock name").when(fail4eva).getName();
// max 5s, backoff initially at 10ms
BackoffPolicy bop = new CappedExponentialBackoff(10, 5000);
final EventSink insistent = new InsistentAppendDecorator<EventSink>(
fail4eva, bop);
final EventSink sink = new LazyOpenDecorator<EventSink>(insistent);
sink.open();
// create an endless stream of data
final EventSource source = new EventSource.Base() {
@Override
public Event next() {
return e;
}
};
// no need to open this.
DirectDriver driver = new DirectDriver(source, sink);
driver.start();
Clock.sleep(1000); // let the insistent append try a few times.
driver.stop();
boolean closed = driver.join(1000);
assertFalse(closed);
driver.cancel();
assertTrue(driver.join(1000)); // closed this time.
}
/**
* This checks to make sure that even though there is along roll period, the
* sink exits quickly due to the cancel call.
*/
@Test
public void testRollSinkCancel() throws IOException, InterruptedException {
EventSink fail4eva = mock(EventSink.Base.class);
final Event e = new EventImpl("foo".getBytes());
doThrow(new IOException("mock exception")).when(fail4eva).append(e);
doReturn(new ReportEvent("mock report")).when(fail4eva).getMetrics();
doReturn("mock name").when(fail4eva).getName();
// max 5s, backoff initially at 10ms
BackoffPolicy bop = new CappedExponentialBackoff(10, 5000);
final EventSink insistent = new InsistentAppendDecorator<EventSink>(
fail4eva, bop);
final EventSink sink = new LazyOpenDecorator<EventSink>(insistent);
final EventSink roll = new RollSink(new ReportTestingContext(), "mock",
10000, 100) {
@Override
public EventSink newSink(Context ctx) throws IOException {
return sink;
}
};
roll.open();
// create an endless stream of data
final EventSource source = new EventSource.Base() {
@Override
public Event next() {
return e;
}
};
// no need to open this.
DirectDriver driver = new DirectDriver(source, roll);
driver.start();
Clock.sleep(1000); // let the insistent append try a few times.
driver.stop();
boolean closed = driver.join(1000);
assertFalse(closed);
driver.cancel();
assertTrue(driver.join(1000)); // closed this time.
}
/**
* Test a thread cancel on something blocked on open. This forces a
* InterruptedException throw. Normally an insistentOpen will never return if
* the subsink's open always fails. cancel() forces the interrupt flag
* (InterruptedException on blocked things) which the insistent open
* translates into a IOException.
*
* (Ideally it should propagate the InterruptedException, but I think that
* change is pervasive and will wait for the next major version)
*/
@Test
public void testDFOSubsinkCancel() throws IOException, InterruptedException {
EventSink fail4eva = mock(EventSink.Base.class);
doThrow(new IOException("mock exception")).when(fail4eva).open();
doReturn(new ReportEvent("stub")).when(fail4eva).getMetrics();
// max 5s, backoff initially at 10ms
BackoffPolicy bop = new CappedExponentialBackoff(10, 5000);
final InsistentOpenDecorator<EventSink> insistent = new InsistentOpenDecorator<EventSink>(
fail4eva, bop);
final StubbornAppendSink<EventSink> stubborn = new StubbornAppendSink<EventSink>(
insistent);
final InsistentAppendDecorator<EventSink> append = new InsistentAppendDecorator<EventSink>(
stubborn, new CappedExponentialBackoff(100, 100000));
final EventSink sink = new LazyOpenDecorator<EventSink>(append);
sink.open();
// create an endless stream of data
final EventSource source = new LazyOpenSource<EventSource>(
new NoNlASCIISynthSource(0, 100));
source.open();
DirectDriver driver = new DirectDriver(source, sink);
driver.start();
Clock.sleep(1000); // let the insistent open try a few times.
driver.stop();
boolean closed = driver.join(1000);
assertFalse(closed);
driver.cancel();
assertTrue(driver.join(1000)); // closed this time.
}
}