/* * #! * % * Copyright (C) 2014 - 2016 Humboldt-Universität zu Berlin * % * Licensed 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 de.hub.cs.dbis.aeolus.batching.api; import java.util.ArrayList; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import backtype.storm.Config; import backtype.storm.task.OutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.IRichBolt; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.TupleImpl; import de.hub.cs.dbis.aeolus.batching.AbstractBatchCollector; import de.hub.cs.dbis.aeolus.batching.BatchColumn; /** * {@link InputDebatcher} enables a bolt to receives input from a producer spout or bolt that emits batches via * {@link SpoutOutputBatcher} or {@link BoltOutputBatcher}, respectively. {@link InputDebatcher} extracts each tuple of * a batch and forwards it to its wrapped bolt for processing. {@link InputDebatcher} can handle and combination of * batched and non-batched input and works with any batch size.<br /> * <br /> * <strong>CAUTION:</strong>Tuple acking, failing, and anchoring is currently not supported. * * @author mjsax */ public class InputDebatcher implements IRichBolt { private final static long serialVersionUID = 7781347435499103691L; private final static Logger logger = LoggerFactory.getLogger(InputDebatcher.class); /** * The bolt that is wrapped. */ private final IRichBolt wrappedBolt; /** * The current runtime environment. */ private TopologyContext topologyContext; /** * Instantiates a new {@link InputDebatcher} that wraps the given bolt. * * @param bolt * The bolt to be wrapped. */ public InputDebatcher(IRichBolt bolt) { this.wrappedBolt = bolt; } @Override public void prepare(@SuppressWarnings("rawtypes") Map stormConf, TopologyContext context, OutputCollector collector) { this.topologyContext = context; this.wrappedBolt.prepare(stormConf, context, collector); } /** * Processes a single input tuple or batch. In case of an regular input tuple, the input tuple is simply forwarded * to the wrapped bolt for processing. In case of an input batch, all tuples are extracted from the batch and * forwarded to the wrapped bolt one by one. The batch metadata is recreated for each extracted tuple. */ @Override public void execute(Tuple input) { logger.trace("input: {}", input); // we cannot check "input.getValues() instanceof Batch", because Storm does not preserve this information // (the class of input.getValues() is always java.utils.ArrayList) if(input.size() > 0 && input.getValue(0) instanceof BatchColumn) { logger.trace("debatching"); final int numberOfAttributes = input.size(); logger.trace("numberOfAttributes: {}", new Integer(numberOfAttributes)); final BatchColumn[] columns = new BatchColumn[numberOfAttributes]; for(int i = 0; i < numberOfAttributes; ++i) { columns[i] = (BatchColumn)input.getValue(i); } final int size = columns[0].size(); logger.trace("batchSize: {}", new Integer(size)); for(int i = 0; i < size; ++i) { final ArrayList<Object> attributes = new ArrayList<Object>(numberOfAttributes); for(int j = 0; j < numberOfAttributes; ++j) { attributes.add(columns[j].get(i)); } logger.trace("extracted tuple #{}: {}", new Integer(i), attributes); final TupleImpl tuple = new TupleImpl(this.topologyContext, attributes, input.getSourceTask(), input.getSourceStreamId()); this.wrappedBolt.execute(tuple); } } else { this.wrappedBolt.execute(input); } } @Override public void cleanup() { this.wrappedBolt.cleanup(); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { this.wrappedBolt.declareOutputFields(declarer); } @Override public Map<String, Object> getComponentConfiguration() { return this.wrappedBolt.getComponentConfiguration(); } /** * Registers internally used classes for serialization and deserialization. Same as * {@link SpoutOutputBatcher#registerKryoClasses(Config)} and {@link BoltOutputBatcher#registerKryoClasses(Config)}. * * @param stormConfig * The storm config the which the classes should be registered to. */ public static void registerKryoClasses(Config stormConfig) { AbstractBatchCollector.registerKryoClasses(stormConfig); } }