/**
 * 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.storm.kafka.trident;

import org.apache.storm.task.OutputCollector;
import org.apache.storm.topology.FailedException;
import org.apache.commons.lang.Validate;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.storm.kafka.trident.mapper.TridentTupleToKafkaMapper;
import org.apache.storm.kafka.trident.selector.KafkaTopicSelector;
import org.apache.storm.trident.operation.TridentCollector;
import org.apache.storm.trident.state.State;
import org.apache.storm.trident.tuple.TridentTuple;

import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class TridentKafkaState implements State {
    private static final Logger LOG = LoggerFactory.getLogger(TridentKafkaState.class);

    private KafkaProducer producer;
    private OutputCollector collector;

    private TridentTupleToKafkaMapper mapper;
    private KafkaTopicSelector topicSelector;

    public TridentKafkaState withTridentTupleToKafkaMapper(TridentTupleToKafkaMapper mapper) {
        this.mapper = mapper;
        return this;
    }

    public TridentKafkaState withKafkaTopicSelector(KafkaTopicSelector selector) {
        this.topicSelector = selector;
        return this;
    }

    @Override
    public void beginCommit(Long txid) {
        LOG.debug("beginCommit is Noop.");
    }

    @Override
    public void commit(Long txid) {
        LOG.debug("commit is Noop.");
    }

    public void prepare(Properties options) {
        Validate.notNull(mapper, "mapper can not be null");
        Validate.notNull(topicSelector, "topicSelector can not be null");
        producer = new KafkaProducer(options);
    }

    public void updateState(List<TridentTuple> tuples, TridentCollector collector) {
        String topic = null;
        for (TridentTuple tuple : tuples) {
            try {
                topic = topicSelector.getTopic(tuple);

                if(topic != null) {
                    Future<RecordMetadata> result = producer.send(new ProducerRecord(topic,
                            mapper.getKeyFromTuple(tuple), mapper.getMessageFromTuple(tuple)));
                    try {
                        result.get();
                    } catch (ExecutionException e) {
                        String errorMsg = "Could not retrieve result for message with key = "
                                + mapper.getKeyFromTuple(tuple) + " from topic = " + topic;
                        LOG.error(errorMsg, e);
                        throw new FailedException(errorMsg, e);
                    }
                } else {
                    LOG.warn("skipping key = " + mapper.getKeyFromTuple(tuple) + ", topic selector returned null.");
                }
            } catch (Exception ex) {
                String errorMsg = "Could not send message with key = " + mapper.getKeyFromTuple(tuple)
                        + " to topic = " + topic;
                LOG.warn(errorMsg, ex);
                throw new FailedException(errorMsg, ex);
            }
        }
    }
}

@@ -1498,16 +1498,44 @@
 Tuple;%0A%0A
+import java.util.ArrayList;%0A
 import j
@@ -2822,16 +2822,117 @@
 = null;%0A
+        try %7B%0A            List%3CFuture%3CRecordMetadata%3E%3E futures = new ArrayList%3C%3E(tuples.size());%0A    
         
@@ -2967,34 +2967,16 @@
 ples) %7B%0A
-            try %7B%0A
         
@@ -3271,56 +3271,380 @@
     
-try %7B%0A                        result
+futures.add(result);%0A                %7D else %7B%0A                    LOG.warn(%22skipping key = %22 + mapper.getKeyFromTuple(tuple) + %22, topic selector returned null.%22);%0A                %7D%0A            %7D%0A%0A            for (int i = 0 ; i %3C futures.size(); i++) %7B%0A                Future%3CRecordMetadata%3E future = futures
 .get(
+i
 );%0A    
+            try %7B%0A                    future.get();%0A
     
@@ -3684,28 +3684,24 @@
 eption e) %7B%0A
-    
             
@@ -3806,20 +3806,16 @@
         
-   
- 
 + mapper
@@ -3832,24 +3832,32 @@
 mTuple(tuple
+s.get(i)
 ) + %22 from t
@@ -3890,28 +3890,24 @@
             
- 
-   
 LOG.error(er
@@ -3910,36 +3910,32 @@
 r(errorMsg, e);%0A
-    
                 
@@ -3986,36 +3986,32 @@
                 
-  
-  
 %7D%0A              
@@ -4012,160 +4012,10 @@
     
-    %7D else %7B%0A                    LOG.warn(%22skipping key = %22 + mapper.getKeyFromTuple(tuple) + %22, topic selector returned null.%22);%0A                %7D%0A    
+%7D%0A
     
@@ -4039,28 +4039,24 @@
 ption ex) %7B%0A
-    
             
@@ -4100,77 +4100,20 @@
 sage
- with key = %22 + mapper.getKeyFromTuple(tuple)%0A                       
+s %22 + tuples
  + %22
@@ -4127,36 +4127,32 @@
 ic = %22 + topic;%0A
-    
             LOG.
@@ -4175,36 +4175,32 @@
 x);%0A            
-    
 throw new Failed
@@ -4224,30 +4224,16 @@
 g, ex);%0A
-            %7D%0A
         

optimize TridentKafkaState batch sending