/** * 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 java.io.IOException; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cloudera.flume.core.Driver; import com.cloudera.flume.core.DriverListener; import com.cloudera.flume.core.Event; import com.cloudera.flume.core.EventSink; import com.cloudera.flume.core.EventSource; import com.cloudera.flume.master.StatusManager.NodeState; import com.google.common.base.Preconditions; /** * This connector hooks a source to a sink and allow this connection to be * stopped and started. * * This assumes that sources and sinks are opened before the start() method is * called */ public class DirectDriver extends Driver { static final Logger LOG = LoggerFactory.getLogger(DirectDriver.class); EventSink sink; EventSource source; PumperThread thd; Exception error = null; NodeState state = NodeState.HELLO; final List<DriverListener> listeners = new ArrayList<DriverListener>(); public DirectDriver(EventSource src, EventSink snk) { this("pumper", src, snk); } public DirectDriver(String threadName, EventSource src, EventSink snk) { Preconditions.checkNotNull(src, "Driver Source was invalid"); Preconditions.checkNotNull(snk, "Driver Sink was invalid"); thd = new PumperThread(threadName); this.source = src; this.sink = snk; } class PumperThread extends Thread { volatile boolean stopped = true; public PumperThread(String name) { super(); setName(name + "-" + getId()); } public void run() { EventSink sink = null; EventSource source = null; synchronized (DirectDriver.this) { sink = DirectDriver.this.sink; source = DirectDriver.this.source; } try { stopped = false; error = null; state = NodeState.ACTIVE; LOG.debug("Starting driver " + DirectDriver.this); fireStart(); while (!stopped) { Event e = source.next(); if (e == null) break; sink.append(e); } } catch (Exception e1) { // Catches all exceptions or throwables. This is a separate thread error = e1; stopped = true; LOG.error("Driving src/sink failed! " + DirectDriver.this + " because " + e1.getMessage(), e1); fireError(e1); state = NodeState.ERROR; return; } LOG.debug("Driver completed: " + DirectDriver.this); fireStop(); state = NodeState.IDLE; } } @Override synchronized public void setSink(EventSink snk) { this.sink = snk; } synchronized public EventSink getSink() { return sink; } @Override synchronized public void setSource(EventSource src) { this.source = src; } synchronized public EventSource getSource() { return source; } @Override public synchronized void start() throws IOException { // don't allow thread to be "started twice" if (thd.stopped) { thd.start(); } } public synchronized boolean isStopped() { return thd.stopped; } @Override public synchronized void stop() throws IOException { thd.stopped = true; } /** * Start the mean shutdown. */ public void cancel() { thd.interrupt(); } @Override public void join() throws InterruptedException { join(0); } @Override public boolean join(long ms) throws InterruptedException { final PumperThread t = thd; t.join(ms); return !t.isAlive(); } public Exception getError() { return error; } @Override public NodeState getState() { return state; } /** * Callbacks cannot add or remove ConnectorListeners -- they can cause * deadlocks on the listeners lock if that happens. * * Here we only lock on the 'listeners' object lock. Previously this locked on * the directconnector which could cause deadlocks with the callback. */ @Override public void registerListener(DriverListener listener) { synchronized (listeners) { listeners.add(listener); } } @Override public void deregisterListener(DriverListener listener) { synchronized (listeners) { listeners.remove(listener); } } void fireStart() { synchronized (listeners) { for (DriverListener l : listeners) { l.fireStarted(this); } } } void fireStop() { synchronized (listeners) { for (DriverListener l : listeners) { l.fireStopped(this); } } } void fireError(Exception e) { synchronized (listeners) { for (DriverListener l : listeners) { l.fireError(this, e); } } } @Override public String toString() { return source.getClass().getSimpleName() + " | " + sink.getName(); } }