content: /**
   * 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 com.alibaba.jstorm.window;
  
  import static org.slf4j.LoggerFactory.getLogger;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.concurrent.locks.Lock;
  import java.util.concurrent.locks.ReentrantLock;
  
  import org.slf4j.Logger;
  
  import com.alibaba.jstorm.transactional.state.IKvState;
  
  import backtype.storm.task.TopologyContext;
  import backtype.storm.tuple.Tuple;
  import backtype.storm.utils.Utils;
  
  public abstract class TransactionalWindowedBolt<T extends Tuple, K, V> extends TransactionalBaseWindowBolt<T, K, V> {
      private static final long serialVersionUID = 8310112984058774752L;
  
      private static final Logger LOG = getLogger(TransactionalWindowedBolt.class);
  
      private IRichCheckpointWindowedState<K, V, String> windowedStateManager;
      private Map<TimeWindow, WindowedKvState<K, V>> windowedStates;
  
      // Lock to keep the concurrent correctness between purgewindow thread and execute thread
      private Lock windowUpdateLock;
  
      public void createState(TopologyContext context) {
          windowedStateManager = (IRichCheckpointWindowedState<K, V, String>) Utils.newInstance("com.alibaba.jstorm.hdfs.transaction.WindowedRocksDbHdfsState");
          windowedStateManager.setStateName(String.valueOf(context.getThisTaskId()));
          windowedStateManager.init(context);
          windowedStates = new HashMap<>();
          windowUpdateLock = new ReentrantLock();
      }
  
      public abstract void execute(T tuple, IKvState<K, V> state, TimeWindow window);
      public abstract void purgeWindow(IKvState<K, V> state, TimeWindow window);
  
      @Override
      public Object initWindowState(TimeWindow window) {
          WindowedKvState<K, V> windowedKvState = new WindowedKvState<>(window, windowedStateManager);
          try {
              windowUpdateLock.lock();
              windowedStates.put(window, windowedKvState);
          } finally {
              windowUpdateLock.unlock();
          }
          return windowedKvState;
      }
  
      @Override
      public void execute(T tuple, Object state, TimeWindow window) {
          execute(tuple, (IKvState<K, V>) state, window);
      }
  
      @Override
      public void purgeWindow(Object state, TimeWindow window) {
          purgeWindow((IKvState<K, V>) state, window);
          try {
              windowUpdateLock.lock();
              removeWindow(window);
          } finally {
              windowUpdateLock.unlock();
          }
      }
  
      @Override
      public void initState(Object userState) {
          LOG.info("Begin to restore windows from state: {}", userState);
          restore(userState);
      }
  
      @Override
      public Object finishBatch(long batchId) {
          try {
              windowUpdateLock.lock();
              // flush local cache states into state manager
              for (WindowedKvState<K, V> state : windowedStates.values()) {
                  state.checkpointBatch();
              }
          } finally {
              windowUpdateLock.unlock();
          }
          // checkpoint all states
          windowedStateManager.checkpoint(batchId);
          return null;
      }
  
      @Override
      public Object commit(long batchId, Object userState) {
          String checkpointPath = windowedStateManager.backup(batchId);
          List<Object> userStates = new ArrayList<>();
          userStates.add(checkpointPath);
          userStates.add(windowedStates);
          return userStates;
      }
  
      @Override
      public void rollBack(Object userState) {
          LOG.info("Begin to restore windows from state: {}", userState);
          restore(userState);
      }
  
      @Override
      public void ackCommit(long batchId, long timeStamp) {
          windowedStateManager.remove(batchId);
      }
  
      private void removeWindow(TimeWindow window) {
          windowedStateManager.removeWindow(window);
          windowedStates.remove(window);
      }
  
      private void restore(Object state) {
          String checkpointPath = null;
          if (state != null) {
              List<Object> stateList = (List<Object>) state;
              checkpointPath = (String) stateList.get(0);
  
              try {
                  windowUpdateLock.lock();
                  windowedStates.clear();
                  if (stateList.get(1) != null) {
                      windowedStates = (Map<TimeWindow, WindowedKvState<K, V>>) stateList.get(1);
                      for (WindowedKvState<K, V> window : windowedStates.values()) {
                          window.setWindowedStateManager(windowedStateManager);
                      }
                  }
              } finally {
                  windowUpdateLock.unlock();
              }
          }
          windowedStateManager.restore(checkpointPath);
      }
  
      
  }