/** * 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.conf; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cloudera.flume.agent.AgentFailChainSink; import com.cloudera.flume.agent.AgentSink; import com.cloudera.flume.agent.diskfailover.DiskFailoverDeco; import com.cloudera.flume.agent.durability.NaiveFileWALDeco; import com.cloudera.flume.collector.CollectorSink; import com.cloudera.flume.core.EventSink; import com.cloudera.flume.core.EventSinkDecorator; import com.cloudera.flume.core.FormatterDecorator; import com.cloudera.flume.core.MaskDecorator; import com.cloudera.flume.core.SelectDecorator; import com.cloudera.flume.core.extractors.RegexExtractor; import com.cloudera.flume.core.extractors.SplitExtractor; import com.cloudera.flume.handlers.avro.AvroEventSink; import com.cloudera.flume.handlers.batch.BatchingDecorator; import com.cloudera.flume.handlers.batch.GunzipDecorator; import com.cloudera.flume.handlers.batch.GzipDecorator; import com.cloudera.flume.handlers.batch.UnbatchingDecorator; import com.cloudera.flume.handlers.debug.BenchmarkInjectDecorator; import com.cloudera.flume.handlers.debug.BenchmarkReportDecorator; import com.cloudera.flume.handlers.debug.BloomCheckDecorator; import com.cloudera.flume.handlers.debug.BloomGeneratorDeco; import com.cloudera.flume.handlers.debug.ChokeDecorator; import com.cloudera.flume.handlers.debug.ConsoleEventSink; import com.cloudera.flume.handlers.debug.DelayDecorator; import com.cloudera.flume.handlers.debug.FlakeyEventSink; import com.cloudera.flume.handlers.debug.InMemoryDecorator; import com.cloudera.flume.handlers.debug.InsistentAppendDecorator; import com.cloudera.flume.handlers.debug.InsistentOpenDecorator; import com.cloudera.flume.handlers.debug.IntervalDroppyEventSink; import com.cloudera.flume.handlers.debug.IntervalFlakeyEventSink; import com.cloudera.flume.handlers.debug.LazyOpenDecorator; import com.cloudera.flume.handlers.debug.MultiplierDecorator; import com.cloudera.flume.handlers.debug.NullSink; import com.cloudera.flume.handlers.debug.StubbornAppendSink; import com.cloudera.flume.handlers.debug.TextFileSink; import com.cloudera.flume.handlers.endtoend.AckChecksumChecker; import com.cloudera.flume.handlers.endtoend.AckChecksumInjector; import com.cloudera.flume.handlers.endtoend.ValueDecorator; import com.cloudera.flume.handlers.hdfs.CustomDfsSink; import com.cloudera.flume.handlers.hdfs.DFSEventSink; import com.cloudera.flume.handlers.hdfs.EscapedCustomDfsSink; import com.cloudera.flume.handlers.hdfs.SeqfileEventSink; import com.cloudera.flume.handlers.irc.IrcSink; import com.cloudera.flume.handlers.rpc.RpcSink; import com.cloudera.flume.handlers.syslog.SyslogTcpSink; import com.cloudera.flume.handlers.thrift.ThriftAckedEventSink; import com.cloudera.flume.handlers.thrift.ThriftEventSink; import com.cloudera.flume.handlers.thrift.ThriftRawEventSink; import com.cloudera.flume.master.availability.FailoverChainSink; import com.cloudera.flume.reporter.aggregator.AccumulatorSink; import com.cloudera.flume.reporter.aggregator.CounterSink; import com.cloudera.flume.reporter.ganglia.GangliaSink; import com.cloudera.flume.reporter.histogram.MultiGrepReporterSink; import com.cloudera.flume.reporter.histogram.RegexGroupHistogramSink; import com.cloudera.flume.reporter.history.CountHistoryReporter; import com.cloudera.flume.reporter.sampler.IntervalSampler; import com.cloudera.flume.reporter.sampler.ProbabilitySampler; import com.cloudera.flume.reporter.sampler.ReservoirSamplerDeco; import com.cloudera.util.Pair; /** * This factory builds sink and sink decorators. This implementation requires a * recompile to add new types. */ public class SinkFactoryImpl extends SinkFactory { static final Logger LOG = LoggerFactory.getLogger(SinkFactoryImpl.class); // The actual types are <String, SinkBuilder> static Object[][] sinkList = { // high level sinks. { "hiveCollectorSink", CollectorSink.hiveBuilder() }, { "hiveElasticSearchCollectorSink", CollectorSink.hiveElasticSearchBuilder() }, { "hesMarkerCollectorSink", CollectorSink.hesMarkerBuilder() }, { "collectorSink", CollectorSink.builder() }, { "agentSink", AgentSink.e2eBuilder() }, { "agentE2ESink", AgentSink.e2eBuilder() }, // now with acks { "agentDFOSink", AgentSink.dfoBuilder() }, { "agentBESink", AgentSink.beBuilder() }, { "agentFailoverSink", AgentSink.dfoBuilder() }, { "agentBestEffortSink", AgentSink.beBuilder() }, { "agentE2EChain", AgentFailChainSink.e2eBuilder() }, { "agentDFOChain", AgentFailChainSink.dfoBuilder() }, { "agentBEChain", AgentFailChainSink.beBuilder() }, // autoE2EChain, autoDFOChain and autoBEChains are essentially node // specific "macros", and use let expresion shadowing { "autoBEChain", EventSink.StubSink.builder("autoBEChain") }, { "autoDFOChain", EventSink.StubSink.builder("autoDFOChain") }, { "autoE2EChain", EventSink.StubSink.builder("autoE2EChain") }, { "logicalSink", EventSink.StubSink.builder("logicalSink") }, // low level sinks { "null", NullSink.builder() }, // all calls throw exception { "fail", EventSink.StubSink.builder("fail") }, { "console", ConsoleEventSink.builder() }, { "text", TextFileSink.builder() }, { "seqfile", SeqfileEventSink.builder() }, { "dfs", DFSEventSink.builder() }, // escapes { "customdfs", CustomDfsSink.builder() }, // does not escape { "escapedCustomDfs", EscapedCustomDfsSink.builder() }, // escapes { "rpcSink", RpcSink.builder() }, //creates AvroEventSink or ThriftEventSink { "syslogTcp", SyslogTcpSink.builder() }, { "irc", IrcSink.builder() }, { "thriftSink", ThriftEventSink.builder() }, { "avroSink", AvroEventSink.builder() }, // advanced { "failChain", FailoverChainSink.builder() }, // @deprecated // reports { "ganglia", GangliaSink.builder() }, { "counter", CounterSink.builder() }, { "accumulator", AccumulatorSink.builder() }, { "counterHistory", CountHistoryReporter.builder() }, { "multigrep", MultiGrepReporterSink.builderSimple() }, { "multigrepspec", MultiGrepReporterSink.builder() }, { "regexhisto", RegexGroupHistogramSink.builderSimple() }, { "regexhistospec", RegexGroupHistogramSink.builder() }, // deprecated { "tsink", ThriftEventSink.builder() }, { "tacksink", ThriftAckedEventSink.builder() }, { "trawsink", ThriftRawEventSink.builder() }, }; // The actual types are <String, SinkDecoBuilder> static Object[][] decoList = { { "nullDeco", EventSinkDecorator.nullBuilder() }, { "diskFailover", DiskFailoverDeco.builder() }, { "ackedWriteAhead", NaiveFileWALDeco.builderEndToEndDir() }, { "ackChecker", AckChecksumChecker.builder() }, { "ackInjector", AckChecksumInjector.builder() }, { "lazyOpen", LazyOpenDecorator.builder() }, { "insistentOpen", InsistentOpenDecorator.builder() }, { "insistentAppend", InsistentAppendDecorator.builder() }, { "stubbornAppend", StubbornAppendSink.builder() }, // relational algebra projection { "value", ValueDecorator.builder() }, { "mask", MaskDecorator.builder() }, { "select", SelectDecorator.builder() }, // TODO (jon) relational algebra selection (filtering) // format the output { "format", FormatterDecorator.builder() }, // extractors { "regex", RegexExtractor.builder() }, { "split", SplitExtractor.builder() }, // cpu / network tradeoffs { "batch", BatchingDecorator.builder() }, { "unbatch", UnbatchingDecorator.builder() }, { "gzip", GzipDecorator.builder() }, { "gunzip", GunzipDecorator.builder() }, // sampling { "intervalSampler", IntervalSampler.builder() }, { "probSampler", ProbabilitySampler.builder() }, { "reservoirSampler", ReservoirSamplerDeco.builder() }, // debugging related { "flakeyAppend", FlakeyEventSink.builder() }, { "intervalFlakeyAppend", IntervalFlakeyEventSink.builder() }, { "intervalDroppyAppend", IntervalDroppyEventSink.builder() }, { "inmem", InMemoryDecorator.builder() }, { "benchinject", BenchmarkInjectDecorator.builder() }, { "benchreport", BenchmarkReportDecorator.builder() }, { "bloomGen", BloomGeneratorDeco.builder() }, { "bloomCheck", BloomCheckDecorator.builder() }, { "mult", MultiplierDecorator.builder() }, { "delay", DelayDecorator.builder() }, { "choke", ChokeDecorator.builder() }, }; Map<String, SinkBuilder> sinks = new HashMap<String, SinkBuilder>(); Map<String, SinkDecoBuilder> decos = new HashMap<String, SinkDecoBuilder>(); public SinkFactoryImpl() { for (Object[] entry : sinkList) { String key = (String) entry[0]; SinkBuilder value = (SinkBuilder) entry[1]; sinks.put(key, value); } for (Object[] entry : decoList) { String key = (String) entry[0]; SinkDecoBuilder value = (SinkDecoBuilder) entry[1]; decos.put(key, value); } String classes = FlumeConfiguration.get().getPluginClasses(); if (!classes.equals("")) { for (String s : classes.split(",")) { loadPluginBuilders(s); } } } @Override public Set<String> getSinkNames() { return Collections.unmodifiableSet(sinks.keySet()); } @Override public Set<String> getDecoratorNames() { return Collections.unmodifiableSet(decos.keySet()); } /** * Given a fully qualified classname, load the class it represents and try and * invoke two methods to get a list of sinks and builders. * * This should only be called at startup to avoid dependency issues; there is * no lazy loading. * * Any exceptions are simply logged and ignored. This is an undocumented * feature for now. */ @SuppressWarnings("unchecked") protected void loadPluginBuilders(String clsName) { try { Class<Object> cls = (Class<Object>) Class.forName(clsName); try { Method mth = cls.getMethod("getSinkBuilders"); List<Pair<String, SinkBuilder>> builders = (List<Pair<String, SinkBuilder>>) mth .invoke(mth); for (Pair<String, SinkBuilder> snk : builders) { LOG.info("Found sink builder " + snk.getLeft() + " in " + clsName); sinks.put(snk.getLeft(), snk.getRight()); } } catch (NoSuchMethodException e) { LOG.warn("No sink builders found in " + clsName); } catch (Exception e) { LOG.error("Error invoking getSinkBuilders on class " + clsName, e); } try { Method mth = cls.getMethod("getDecoratorBuilders"); List<Pair<String, SinkDecoBuilder>> builders = (List<Pair<String, SinkDecoBuilder>>) mth .invoke(mth); for (Pair<String, SinkDecoBuilder> snk : builders) { LOG.info("Found sink decorator " + snk.getLeft() + " in " + clsName); decos.put(snk.getLeft(), snk.getRight()); } } catch (NoSuchMethodException e) { LOG.warn("No sink decorators found in " + clsName); } catch (Exception e) { LOG.error("Error invoking getDecoratorBuilders on class " + clsName, e); } } catch (ClassNotFoundException e) { LOG.error("Could not find class " + clsName + " for plugin loading", e); } } @Override public EventSinkDecorator<EventSink> getDecorator(Context context, String name, String... args) throws FlumeSpecException { try { SinkDecoBuilder builder = decos.get(name); if (builder == null) return null; return builder.build(context, args); } catch (NumberFormatException nfe) { throw new FlumeArgException("Illegal number format: " + nfe.getMessage()); } catch (IllegalArgumentException iae) { throw new FlumeArgException(iae.getMessage()); } } @Override public EventSink getSink(Context context, String name, String... args) throws FlumeSpecException { try { SinkBuilder builder = sinks.get(name); if (builder == null) return null; return builder.build(context, args); } catch (NumberFormatException nfe) { throw new FlumeArgException("Illegal number format: " + nfe.getMessage()); } catch (IllegalArgumentException iae) { throw new FlumeArgException(iae.getMessage()); } } /** * This is only for testing */ public void setSink(String name, SinkBuilder builder) { sinks.put(name, builder); } /** * This is only for testing */ public void setDeco(String name, SinkDecoBuilder builder) { decos.put(name, builder); } }